Andorid基础笔记2-jar&反射

生成android 动态加载的jar包

方式1 脚本文件生成

复制代码
- 用途:Android 运行时直接加载、插件化、热修复、Android 插件
- 特点:内部包含 classes.dex,手机能直接执行
- 在根目录下创建脚本
    - 设置文件名字      set filename=xxx.jar
    - 设置源文件目录    set srcDir=全路径
    - 设置dx路径        set dx=全路径\dx.bat
    - 删除输出目录文件  del out\%filename%
    - 执行 dx 打包      call %dx% --dex --output=out\%filename% %srcDir%
    - 等待退出          pause

方式2 Gradle标准打包

复制代码
- 用途:纯 Java 代码库、给其他模块依赖、做 SDK 基础包
- 特点:只有 .class 文件,没有 dex,不能直接在 Android 运行
- 在模块下的build.gradle 添加
    task makeJar(type: Copy){
        //删除已存在的 jar
        delete 'build/libs/test.jar'

        //设置拷贝文件
        from('build/intermediates/aar_main_jar/debug/syncDebugLibJars/')

        //打进jar包的文件目录
        into('build/libs/')
        include('classes.jar')

        //重命名
        rename('classes.jar', 'test.jar')
    }
    然后再根目录下执行 gradlew makeJar
 - 如遇到版本不兼容问题
    在根目录下gradle.properties中添加
        org.gradle.java.home=安装路径下/jbr

java 加载jar包

内部加载jar

复制代码
- 需要再main目录先新建assets目录
- 把需要的jar放到目录中
- 不需要权限,最稳定
- 使用方式 复制到内部--> 动态加载 + 反射
- 加载流程
    - InputStream is = getAssets().open("xxxx.jar"); //获取assets目录下的jar包
    - File destFile = new File(getFilesDir(), "xxxx.jar"); //获取应用私有内部存储目录,?默认路径?:/data/user/0/<包名>/files/
    - Files.copy(is, destFile.toPath(), StandardCopyOption.REPLACE_EXISTING); //用 Files 或 IO 工具类复制

外部加载jar

复制代码
- 放在/sdcard/Download/目录下
- 需要权限(Android 6.0 以后 必须满足两个条件才生效:静态申请+动态申请)
    - 静态申请权限(在清单文件写入)
        - <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
        - <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    - 动态权限申请
        -  ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 100);
- 支持热更新,替换jar就生效
- 使用方式 直接路径--> 动态加载 + 反射

通用的加载流程

复制代码
- 如果内部加载 需要先拷贝,外部加载直接指定绝对路径即可,然后都是统一下面的流程
- private File optimizedDir;
- optimizedDir = getDir("自定义名字", MODE_PRIVATE);
- DexClassLoader loader = new DexClassLoader(dexPath, optimizedDir.getAbsolutePath(), null, getClassLoader());
- Class<?> clazz = loader.loadClass("a.b.c.mylibrary.Test"); //加载类 报名+类名
- Object ins_Test = clazz.newInstance(); //实例化类
- Method meth_showFunc = clazz.getMethod("showFunc", int.class); //获取方法, 第一个方法名,后面是方法参数...
- meth_showFunc.invoke(null); //static方法参数传null, 常规方法参数传实例化类属性(如: ins_Test)

NDK JNI 加载jar包

通用的加载流程

复制代码
- 拿到当前的加载器
    - jclass cls = env->GetObjectClass(thiz); //拿到当前的Activity的类
    - jmethodID mid = env->GetMethodID(cls, "getClassLoader", "()Ljava/lang/ClassLoader;") //找到getClassLoader() 方法
    - jobject parentLoader = env->CallObjectMethod(thiz, mid); //调用,获取父加载器

- 创建DexClassLoader 去加载jar文件
    - jstring jarPath = env->NewStringUTF("jar的全路径"); //jar路径(必须是真实文件,不能是assets)
    - jstring optDir = env->NewStringUTF("/data/data/包名/app_dex"); //优化输出目录

    - jclass loaderCls = env->findClass("dalvik/system/DexClassLoader"); //找到DexClassLoader类
    - jmethodID ctor = env->GetMethodID(loaderCls, "<init>", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)V"); //找到构造方法
    - jobjcet dexLoader = env->NewObject(loaderCls, ctor, jarPath, optDir, NULL, parentLoader) //创建加载类

- 加载jar里面的类
    - jmethodID mid_loadClass = env->GetMethodID(loaderCls, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); //找到loadClass方法
    - jstring cls_name = env->NewStringUTF("包名.类名") //类名
    - jobject tmpClass = (jclass)env->CallObjectMethod(dexLoader, mid_loadClass, cls_name); //加载类

- 创建对象
    - jobject ins_obj = env->AllocObject(tmpClass); //这是类的实例

- 方法操作
    - jmethodID meth1 = env->GetMethodID(tmpClass, "方法名", "签名"); //找到常规方法
    - env->CallVoidMethod(ins_obj, meth1); //调用方法(有多中类型 根据真实方法返回值类型选择), 有参数在后面填写
    - jmethodID meth2 = env->GetStaticMethodID(tmpClass, "方法名", "签名"); //找到静态方法
    - env->CallStaticVoidMethod(tmpClass, meth2); //调用方法(有多中类型 根据真实方法返回值类型选择), 有参数在后面填写

- 属性操作
    - jfieldID fid_age = env->GetFieldID(tmpClass, "age", "I"); //获取属性ID
    - env->SetIntField(ins_obj, fid_age, 11); //直接设置属性
    - jint age = env->GetIntField(ins_obj, fid_age); //直接读取属性
    - 属性不同类型对照
        - java属性  --> JNI函数         --> 签名
        - int       --> GetIntField     --> I
        - boolean	--> GetBooleanField	--> Z
        - String	--> GetObjectField	--> Ljava/lang/String;
        - float	    --> GetFloatField	--> F
        - 对象	    --> GetObjectField	--> L 全类名;

JNI 接口函数

复制代码
- JNI函数的2个默认参数(所有函数都有)
    - JNIEnv *env //JNI环境(所有操作都靠它)
    - jobject thiz //Java 调用者(this)

- 最核心5大类接口(能写任何NDK代码)
    - 查找类(FindClass)
        - 作用: 通过类名找到Java类
        - 格式:jclass env->FindClass("类路径用/分隔");
    - 获取方法(GetMethodID)
        - 作用:获取Java方法的ID(必须反射)
        - 格式:jmethodID env->GetMethodID(jclass clazz, const char *name, const char *sig);// 参数1: 类  参数2: 方法名  参数3: 方法签名
        - 例子:jmethodID mid = env->GetMethodID(cls, "setAge", "(I)V"); //普通方法例子
                jmethodID env->GetStaticMethodID(cls, "setAge", "(I)V"); //静态方法例子
                jmethodID ctor = env->GetMethodID(cls, "<init>", "签名"); //构造方法例子
    - 创建对象(AllocObject/NewObject)
        - 格式:jobject ins_obj = AllocObject(jclass clazz);
        - 格式:jobject ins_obj = NewObject(jclass, jmethodID, 参数...); 
        - 特点:AllocObject不调用构造,适合构造方法为空或者里面逻辑不重要,只需要一个实例,用来调用普通方法或者成员属性;
                NewObject调用构造,适合类的构造方法必须执行才能正常使用,需要先获取对应的构造方法MethodID;
    - 调用方法(CallXXXMethod)
        - 作用:调用Java方法
        - 格式:(返回值类型) env->CallVoidMethod(对象, 方法ID, 参数...); //常规方法调用 返回值类型(Jint, jboolean等)
        - 格式:(返回值类型) env->CallStaticVoidMethod(类, 方法ID, 参数...); //静态方法调用 返回值类型(Jint, jboolean等)
    - 属性操作(Get/SetField)
        - 作用:读写Java属性
        - 格式:jfieldID env->GetFieldID(类, 属性名, 签名); //获取属性ID
        - 格式:(返回值类型) env->GetXXXField(对象, 属性ID); //读取属性值
        - 格式:env->SetXXXField(对象, 属性ID, 值);

- 字符串操作(必用)
    - 把C语言字符串 --> 转换成java的String对象
        - 格式:jstring NewStringUTF(const char *bytes);
    - 把Java的String --> 转换成C语言字符串
        - 格式:const char *GetStringUTFChars(jstring  string, jboolean* isCopy);
    - 释放字符串(必须与GetStringUTFChars成对使用, 否则内存泄漏)
        - 格式:void ReleaseStringUTFChars(jstring string, const char *chars);
    - 获取Java字符串长度
        - 格式:jsize GetStringLength(jstring string);
相关推荐
智者知已应修善业4 小时前
【51单片机单按键切换广告屏】2023-5-17
c++·经验分享·笔记·算法·51单片机
ShawnLiaoking4 小时前
pycharm 上传更新代码
ide·elasticsearch·pycharm
凉、介5 小时前
别再把 PCIe 的 inbound/outbound、iATU 和 eDMA 混为一谈
linux·笔记·学习·嵌入式·pcie
雷工笔记7 小时前
MES / WMS / AGV 交互时序图及生产管理模块界面设计清单
人工智能·笔记
大邳草民7 小时前
Python 中 global 与 nonlocal 的语义与机制
开发语言·笔记·python
landuochong2007 小时前
claude-obsidian 再升级
人工智能·笔记·claudecode
CheerWWW8 小时前
C++学习笔记——线程、计时器、多维数组、排序
c++·笔记·学习
ljt27249606618 小时前
Compose笔记(七十六)--拍照预览
笔记·android jetpack
ZC跨境爬虫8 小时前
dankoe视频笔记:如何培养对自己喜欢之事的痴迷感
人工智能·笔记·搜索引擎