文章目录
I . 动态注册流程 ( 总结 )
II . JNI_OnLoad 方法
III . 被注册的本地 C/C++ 方法参数
IV . JNINativeMethod 结构体 ( 核心重点 )
V . JavaVM 获取 JNIEnv ( GetEnv )
VI . 动态注册方法 RegisterNatives ( 核心重点 )
VII . 动态注册流程完整代码
I . 动态注册流程 ( 总结 )
动态注册流程 :
① 声明 Java 层 Native 方法 : 在 Java 类中声明 native 方法 ;
/** * 动态注册 JNI 方法 , Java 方法 */ public native void dynamicRegisterJavaMethod(); public native int dynamicRegisterJavaMethod2(int i);
② 准备数据 JNINativeMethod methods[] 数组 : 其中定义了 Java 层方法与 Native 层方法的对应关系 ;
/* 该数组中 , 每个元素都是一个 JNI 的 Native 方法 JNINativeMethod 是结构体 typedef struct { const char* name; //Java 中定义的 Native 方法名 , 注意这是一个 C 字符串 const char* signature; //函数签名 , 可以使用 javap 生成 void* fnPtr; //C/C++ 中的 Native 函数签名 } JNINativeMethod; */ static const JNINativeMethod methods[] = { {"dynamicRegisterJavaMethod", "()V", (void *)dynamicRegisterCMethod}, {"dynamicRegisterJavaMethod2", "(I)I", (void *)dynamicRegisterCMethod2} };
③ 编写 JNI_OnLoad 方法 : 在该方法中进行 JNI 方法动态注册操作 ;
int JNI_OnLoad(JavaVM *vm , void *r){ return JNI_VERSION_1_6; }
④ 获取 JNIEnv 指针 : 调用 JavaVM 结构体的 GetEnv 方法 , 获取 JNIEnv 指针 ;
//1 . 获取 JNIEnv JNI 环境 , 需要从 JavaVM 获取 JNIEnv *env = nullptr; //2 . 调用 JavaVM / _JavaVM 结构体的 jint GetEnv(void** env, jint version) 方法 // 返回值分析 : 动态注册会返回一个结果 // 如果 registerResult < 0 , 则动态注册失败 // 如果 registerResult == 0 , 则动态注册失败 int registerResult = vm->GetEnv( (void **) &env, JNI_VERSION_1_6 );
⑤ 获取 Java 类 : 调用 JNIEnv 结构体的 FindClass 方法获取 jclass 对象 ;
/* 动态注册的 Java 类名称 注意 : 包名类名之间使用 "/" 分割 */ static const char* className = "kim/hsl/onload/MainActivity"; //获取要动态注册的 Java 类的 Class 对象 jclass jclazz = env->FindClass(className);
⑥ 进行动态注册 : 调用 JNIEnv 的 RegisterNatives 方法 , 进行正式注册 ;
/* 5 .正式注册 注册方法解析 : jint RegisterNatives( jclass clazz, //要注册的 Java 类 const JNINativeMethod* methods, //JNI注册方法数组 jint nMethods //要注册的 JNI 方法个数 ) sizeof(methods) / sizeof(JNINativeMethod) : 计算 JNINativeMethod methods[] 数组大小 */ env->RegisterNatives(jclazz, methods, sizeof(methods) / sizeof(JNINativeMethod));
II . JNI_OnLoad 方法
1 . JNI_OnLoad 函数原型 : Java 类中调用 System.loadLibrary(“native-lib”) 代码时 , 调用 JNI_OnLoad 方法 ;
① jni.h 中有该函数的声明 :
/* * Prototypes for functions exported by loadable shared libs. These are * called by JNI, not provided by JNI. */ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved);
② 参数列表说明 :
JavaVM* vm : 表示 Java 虚拟机 ;
void* reserved : 一般是 NULL ;
③ 返回值说明 : 返回当前 NDK 使用的 JNI 版本 ;
JNI 版本 中可选的有四个值 , 但是只能选择返回后三个 JNI_VERSION_1_2 , JNI_VERSION_1_4 , JNI_VERSION_1_6 , 返回上述三个值任意一个没有区别 ;
返回 JNI_VERSION_1_1 会报错 ;
#define JNI_VERSION_1_1 0x00010001 #define JNI_VERSION_1_2 0x00010002 #define JNI_VERSION_1_4 0x00010004 #define JNI_VERSION_1_6 0x00010006
2 . JNI_OnLoad 方法常用操作 :
① 获取 JavaVM 对象 ;
② 动态注册 JNI 方法 ;
III . 被注册的本地 C/C++ 方法参数
1 . 动态注册对应的 C/C++ 本地方法 参数情况 :
① 传递参数 : 如果动态注册的方法需要传递参数 , 需要加上 前面的 JNIEnv *env, jobject obj 两个参数
jint dynamicRegisterCMethod2(JNIEnv *env, jobject obj, jint i){ __android_log_print(ANDROID_LOG_INFO, "JNI_TAG", "dynamicRegisterCMethod2 : %d", i); return i + 1; }
② 不传递参数 : 如果不传递参数 , 就可以不添加任何参数 , 参数可以空着 ;
void dynamicRegisterCMethod(){ __android_log_print(ANDROID_LOG_INFO, "JNI_TAG", "dynamicRegisterCMethod"); }
IV . JNINativeMethod 结构体 ( 核心重点 )
1 . 结构体定义 : 该结构体定义了 C/C++ 方法 与 Java 方法的映射 ;
① const char* name : Java 中定义的 Native 方法名 , 注意这是一个 C 字符串
② const char* signature : 函数签名 , 可以使用 javap 生成
③ void* fnPtr : C/C++ 中的 Native 函数指针
typedef struct { const char* name; // Java 中定义的 Native 方法名 , 注意这是一个 C 字符串 const char* signature; // 函数签名 , 可以使用 javap 生成 void* fnPtr; // C/C++ 中的 Native 函数签名 } JNINativeMethod;
2 . JNINativeMethod methods[] 数组 :
该数组是 JNI 方法动态注册的参数 , 每个结构体表示了本地方法 与 Java 层方法的映射 ;
数组中有几个元素 , 那么就映射了几个方法 ;
V . JavaVM 获取 JNIEnv ( GetEnv )
函数原型 : 从 Java 虚拟机 ( JavaVM ) 中获取 JNI 运行环境 ( JNIEnv ) ;
① 参数说明 :
void** env : JNIEnv 的二级指针 ;
jint version : JNI 的版本 , 一般传入 JNI_VERSION_1_6 ;
② 返回值说明 : 返回动态注册结果 ;
动态注册成功 : 返回 JNI_OK , 即 0 ;
动态注册失败 : 返回一个小于 0 的值 ;
struct _JavaVM { //封装了 JNIInvokeInterface 结构体 , C 语言环境中调用该结构体中的方法 const struct JNIInvokeInterface* functions; #if defined(__cplusplus) ... //C++ 中封装了 JNIInvokeInterface 的 GetEnv 方法 jint GetEnv(void** env, jint version) { return functions->GetEnv(this, env, version); } ... #endif /*__cplusplus*/ };
VI . 动态注册方法 RegisterNatives ( 核心重点 )
1 . 函数原型 : 通过该 RegisterNatives 方法注册 JNI 方法 ;
参数 :
jclass clazz : 要注册方法所在的 Java 类 const JNINativeMethod* methods : Java 方法 与 本地方法的映射关系数组 ; jint nMethods : 映射的方法个数 , 一般是 methods 数组元素个数 ; struct _JNIEnv { /* _JNIEnv 结构体中封装了 JNINativeInterface 结构体指针 */ const struct JNINativeInterface* functions; ... // 最终 调用的 还是 JNINativeInterface 结构体中封装的 RegisterNatives 方法 jint RegisterNatives(jclass clazz, const JNINativeMethod* methods, jint nMethods) { return functions->RegisterNatives(this, clazz, methods, nMethods); } ... }
2 . 代码示例 :
//1 . Java 与 本地 方法映射数组 JNINativeMethod methods[] = { {"dynamicRegisterJavaMethod", "()V", (void *)dynamicRegisterCMethod}, {"dynamicRegisterJavaMethod2", "(I)I", (void *)dynamicRegisterCMethod2} }; ... //2 . Java 类名 , 注意 : 包名类名之间使用 "/" 分割 char* className = "kim/hsl/onload/MainActivity"; ... //3 . 获取要动态注册的 Java 类的 Class 对象 jclass jclazz = env->FindClass(className); ... //4 . 正式注册 env->RegisterNatives(jclazz, methods, sizeof(methods) / sizeof(JNINativeMethod));
VII . 动态注册流程完整代码
1 . Java 层代码 :
package kim.hsl.onload; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.widget.TextView; public class MainActivity extends AppCompatActivity { // Used to load the 'native-lib' library on application startup. static { System.loadLibrary("native-lib"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); dynamicRegisterJavaMethod(); dynamicRegisterJavaMethod2(250); } /** * 动态注册 JNI 方法 , Java 方法 */ public native void dynamicRegisterJavaMethod(); public native int dynamicRegisterJavaMethod2(int i); }
2 . 本地代码 ( C++ ) :
#include <jni.h> #include <string> #include <android/log.h> /* I . JNI_Onload 方法 JNI_Onload 方法在 Java 层执行 System.loadLibrary("native-lib") 代码时调用的方法 主要是执行一些 JNI 初始化操作 JNI_Onload 常见操作 : ① 保存 JavaVM 对象 , 使用全局变量记录该 Java 虚拟机对象 ② 动态注册 : 动态注册是该方法中最常见的操作 JNI_Onload 参数说明 : JavaVM *vm : 代表了 Java 虚拟机 void *r : 一般是 NULL JNI_Onload 返回值说明 : int 类型返回值代表了当前 NDK 使用的 JNI 版本 JNI 版本 中可选的有四个值 , 但是只能选择返回后三个 JNI_VERSION_1_2 , JNI_VERSION_1_4 , JNI_VERSION_1_6 返回上述三个值任意一个没有区别 返回 JNI_VERSION_1_1 会报错 #define JNI_VERSION_1_1 0x00010001 #define JNI_VERSION_1_2 0x00010002 #define JNI_VERSION_1_4 0x00010004 #define JNI_VERSION_1_6 0x00010006 这四个值分别代表了 JDK 1.1 , 1.2 , 1.4 , 1.6 对应的 JNI 版本 II . 动态注册 动态注册 : 动态注册与静态注册 : 静态注册 : 使用 Java_包名_类名_方法名(JNIEnv* env, jobject obj, ...) 方式进行注册是静态注册 动态注册 : 将 C/C++ 中的本地方法 与 Java 中的方法对应起来 , 就需要使用动态注册 动态注册 与 静态注册 : 没有太大区别 , 都可以将 C/C++ 本地方法 与 Java 方法对应起来 动态注册流程 : ① 声明 Java 层 Native 方法 ② 准备数据 JNINativeMethod methods[] 数组 ③ 编写 JNI_OnLoad 方法 ④ 获取 JNIEnv 指针 ⑤ 获取 Java 类 ⑥ 进行动态注册 */ //使用 全局变量 记录 Java 虚拟机对象 JavaVM *_vm; /* 动态注册对应的 C/C++ 本地方法 如果动态注册的方法需要传递参数 , 需要加上 前面的 JNIEnv *env, jobject obj 两个参数 如果不传递参数 , 就可以不添加任何参数 不传递参数 , 参数可以空着 */ void dynamicRegisterCMethod(){ __android_log_print(ANDROID_LOG_INFO, "JNI_TAG", "dynamicRegisterCMethod"); } /* 动态注册对应的 C/C++ 本地方法 如果动态注册的方法需要传递参数 , 需要加上 前面的 JNIEnv *env, jobject obj 两个参数 如果不传递参数 , 就可以不添加任何参数 传递参数 , 那么需要写上 JNI 调用的完整参数 */ jint dynamicRegisterCMethod2(JNIEnv *env, jobject obj, jint i){ __android_log_print(ANDROID_LOG_INFO, "JNI_TAG", "dynamicRegisterCMethod2 : %d", i); return i + 1; } /* 该数组中 , 每个元素都是一个 JNI 的 Native 方法 JNINativeMethod 是结构体 typedef struct { const char* name; //Java 中定义的 Native 方法名 , 注意这是一个 C 字符串 const char* signature; //函数签名 , 可以使用 javap 生成 void* fnPtr; //C/C++ 中的 Native 函数指针 } JNINativeMethod; */ static const JNINativeMethod methods[] = { {"dynamicRegisterJavaMethod", "()V", (void *)dynamicRegisterCMethod}, {"dynamicRegisterJavaMethod2", "(I)I", (void *)dynamicRegisterCMethod2} }; /* 动态注册的 Java 类名称 注意 : 包名类名之间使用 "/" 分割 */ static const char* className = "kim/hsl/onload/MainActivity"; int JNI_OnLoad(JavaVM *vm , void* reserved){ __android_log_print(ANDROID_LOG_INFO, "JNI_TAG", "JNI_Onload"); //I . 存储 Java 虚拟机对象 //将 Java 虚拟机对象记录到全局变量中 _vm = vm; //II . 动态注册 //1 . 获取 JNIEnv JNI 环境 , 需要从 JavaVM 获取 JNIEnv *env = nullptr; //2 . 调用 JavaVM / _JavaVM 结构体的 jint GetEnv(void** env, jint version) 方法 // 返回值分析 : 动态注册会返回一个结果 // 如果 registerResult < 0 , 则动态注册失败 // 如果 registerResult == 0 , 则动态注册失败 int registerResult = vm->GetEnv( (void **) &env, JNI_VERSION_1_6 ); //3 . 判断结果 : 如果动态注册 Native 方法失败 , 直接退出 if(registerResult != JNI_OK){ return -1; } //4 . 获取要动态注册的 Java 类的 Class 对象 jclass jclazz = env->FindClass(className); /* 5 .正式注册 注册方法解析 : jint RegisterNatives( jclass clazz, //要注册的 Java 类 const JNINativeMethod* methods, //JNI注册方法数组 jint nMethods //要注册的 JNI 方法个数 ) sizeof(methods) / sizeof(JNINativeMethod) : 计算 JNINativeMethod methods[] 数组大小 */ env->RegisterNatives(jclazz, methods, sizeof(methods) / sizeof(JNINativeMethod)); return JNI_VERSION_1_6; }
3 . 执行结果 :
2020-02-08 14:01:51.142 13375-13375/? I/JNI_TAG: JNI_Onload 2020-02-08 14:01:51.299 13375-13375/? I/JNI_TAG: dynamicRegisterCMethod 2020-02-08 14:01:51.300 13375-13375/? I/JNI_TAG: dynamicRegisterCMethod2 : 250