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);
相关推荐
RainCity4 天前
Java Swing 自定义组件库分享(十二)
java·笔记·后端
zzzzzz3109 天前
当产品经理说这个很简单:我用Python自动化处理奇葩需求的实战指南
python·pycharm·产品经理
LinXunFeng12 天前
Obsidian - 使用 Share Note 分享笔记并自部署
前端·笔记·github
闪闪发亮的小星星16 天前
高斯光以及高斯光公式解释
笔记
cqbzcsq16 天前
CellFlow虚拟细胞论文阅读
论文阅读·人工智能·笔记·学习·生物信息
阿米亚波16 天前
【Windows】QEMU 启动 openEuler aarch64/arm64 架构系统 + 离线软件源
linux·windows·经验分享·笔记·架构·arm
自传.16 天前
尚硅谷 Vibe Coding|第三章(1) Claude Code深度使用与进阶技巧 学习笔记
笔记·学习·尚硅谷·vibecoding
.千余16 天前
【C++】模板进阶全解:非类型参数|全特化|偏特化|分离编译完全指南
开发语言·c++·笔记·学习·其他
自传.16 天前
尚硅谷 Vibe Coding|第二章 AI编程工具生态 学习笔记
笔记·学习·ai编程·尚硅谷·vibe coding
秋波。未央16 天前
Java Agent 开发 · Day 1 学习笔记(含作业完整标准答案)
java·笔记·学习