文章目录

  • 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");}@Overrideprotected 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 : 一般是 NULLJNI_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

【Android NDK 开发】JNI 动态注册 ( 动态注册流程 | JNI_OnLoad 方法 | JNINativeMethod 结构体 | GetEnv | RegisterNatives )相关推荐

  1. 安卓逆向_15( 三 ) --- Android NDK 开发【 jni 静态注册、JNI_OnLoad 动态注册】

    Android Studio开发JNI示例:https://blog.csdn.net/wzhseu/article/details/79683045 JNI_动态注册_静态注册.zip : http ...

  2. 【Android NDK 开发】Android Studio 使用 CMake 导入动态库 ( 构建脚本路径配置 | 指定动态库查找路径 | 链接动态库 )

    文章目录 I . CMake 引入动态库与静态库区别 II . Android Studio 中 CMake 引入动态库流程 III . 指定动态库查找路径 IV . 链接函数库 V . 完整代码示例 ...

  3. 【Android NDK 开发】Android.mk 配置静态库 ( Android Studio 配置静态库 | 配置动态库与静态库区别 | 动态库与静态库打包对比 )

    文章目录 I . Android Studio 中使用 Android.mk 配置静态库 总结 II . 第三方动态库来源 III . 配置 Android.mk 构建脚本路径 IV . 预编译 第三 ...

  4. 【Android NDK 开发】Android.mk 配置动态库 ( Android Studio 配置动态库 | 动态库加载版本限制 | 本章仅做参考推荐使用 CMake 配置动态库 )

    文章目录 I . Android Studio 中使用 Android.mk 配置动态库 总结 II . 第三方动态库来源 III . 配置 Android.mk 构建脚本路径 IV . 预编译 第三 ...

  5. 【Android NDK 开发】NDK 交叉编译 ( Ubuntu 中交叉编译动态库 | Android Studio 中配置使用第三方动态库 )

    文章目录 I . 动态库 与 静态库 II . 编译动态库 III. Android Studio 使用第三方动态库 IV . Android Studio 关键代码 V . 博客资源 I . 动态库 ...

  6. 【Android NDK 开发】Android Studio 使用 CMake 导入静态库 ( CMake 简介 | 构建脚本路径配置 | 引入静态库 | 指定静态库路径 | 链接动态库 )

    文章目录 I . CMake 简介 II . Android Studio 中 CMake 引入静态库流程 III . 指定 CMake 最小版本号 IV . 导入函数库 ( 静态库 / 动态库 ) ...

  7. 【Android NDK 开发】NDK C/C++ 代码崩溃调试 - Tombstone 报错信息日志文件分析 ( 使用 addr2line 命令行工具查找动态库中的报错代码位置 )

    文章目录 一.从 Tombstone 报错日志中查找报错动态库 二.addr2line 命令行工具使用 64 位动态库使用的 aarch64-linux-android-addr2line.exe 工 ...

  8. Android NDK开发之旅(2):一篇文章搞定Android Studio中使用CMake进行NDK/JNI开发

    Android NDK开发之旅(2):一篇文章搞定android Studio中使用CMake进行NDK/JNI开发 (码字不易,转载请声明出处:http://blog.csdn.NET/andrex ...

  9. Android 高级开发 JNI NDK 介绍与使用

    Android 高级开发 JNI & NDK 介绍与使用 前言 对于没接触过的领域,即是挑战也是机遇,不仅能够提升自己的能力.还能够学习到新的技术知识 而学习新的技术的时候,最好是从头开始按照 ...

最新文章

  1. linux cron读哪个文件,linux crontab 文件位置和日志位置
  2. lazada新手开店之后,怎样选择正确类目来发布产品?
  3. 51单片机开发板(W25Q16学习)
  4. Nginx_反向代理配置讲解
  5. VS 使用 :新建项目
  6. android开发UI界面布局教学,android UI学习 -- 设置界面的布局(包括style的使用,selector的使用,Checkbox自定义样式,菜单项的样式)...
  7. PHP正则表达式实例汇总
  8. c++获取一段代码的执行时间_微软IE浏览器JScript脚本引擎远程代码执行漏洞通告...
  9. 为SSD加速 实战4KB对齐技巧3/3
  10. 洛谷 绕钉子的长绳子
  11. 使用vue搭建项目(创建手脚架)
  12. 如何利用matlab根据excel画三维图像
  13. 计算机英语单词练习一
  14. 人人都是产品经理2.0-04章摘要
  15. 在MatLab中FFT和IFFT的互相转换
  16. Terracotta 3.2.1简介 (二)
  17. 痞子衡嵌入式:飞思卡尔i.MX RT系列MCU量产神器RT-Flash用户指南
  18. 会计学原理学习笔记——第三章——账户与复式记账(3.4生产准备业务核算——材料采购业务核算)
  19. Cesium的学习之路(二):底图切换
  20. c语言指针移动函数,fseek c语言指针移动函数

热门文章

  1. springboot 异步不生效
  2. 【转载】Hyperledger学习小结
  3. 异步同步、阻塞非阻塞、异步回调、线程队列和协程
  4. SpringBoot24 SpringDataJPA环境搭建、实体类注解、关联查询
  5. Process management of windows
  6. memcache的安装及管理
  7. 最大公共子串提取“模式”
  8. 用Greasemonkey脚本收藏网站会员信息到本地
  9. string:值类型?引用类型?[转]
  10. windows下多进程加协程并发模式