博客: 安卓之家
微博: 追风917
CSDN: 蒋朋的家
简书: 追风917

最近一直在搞 jni 开发,里面坑挺多,其实都是自己不注意导致的。如果你不知道 jni,但是你又想了解这个坑,请先到隔壁喝个茶,取点经,隔壁地址:

Android 开发 之 JNI入门 - NDK从入门到精通

昨天突发奇想,既然 native 方法的命名是这样的:

    Java_完整包名类名_方法名()

那么如果包名或者类名含有下划线“_”时,会怎么样呢?哈哈哈

很悲剧,按照上面的 native 方法的命名规则,会出现 Native method not found 的异常,那么该如何处理呢?只能保证包名和类名不出现下划线吗?

此时,我又想起了 bunnyblue 大神的名言:

对于程序来言,一切都是可以实现的,就是代价的问题

多么简单粗暴,一切都是可以搞的,那么这个该如何处理呢,就是本篇文章里讲到的动态注册 native 方法

动态注册 native 方法

先上一段代码吧:

static jstring nativeDynamicRegFromJni(JNIEnv *env, jobject obj)
{return (*env) -> NewStringUTF(env, "动态注册调用成功");
}JNINativeMethod nativeMethod[] = {{"dynamicRegFromJni", "()Ljava/lang/String;", (void*)nativeDynamicRegFromJni}};JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved)
{JNIEnv *env;if ((*jvm) -> GetEnv(jvm, (void**) &env, JNI_VERSION_1_4) != JNI_OK){return -1;}jclass clz = (*env) -> FindClass(env, "github/jp1017/hellojni/MainActivity");(*env) -> RegisterNatives(env, clz, nativeMethod, sizeof(nativeMethod) / sizeof(nativeMethod[0]));return JNI_VERSION_1_4;
}

上面就是动态注册的全部代码了,java 代码中的函数是 dynamicRegFromJni,调用的 native 方法是 nativeDynamicRegFromJni,该方法没有参数,返回值是一个字符串。

如果你看不出来,请继续往下看,容我慢慢道来:

动态注册的原理是这样的:JNI 允许我们提供一个函数映射表,注册给 JVM,这样 JVM 就可以用函数映射表来调用相应的函数,
而不必通过函数名来查找相关函数(这个查找效率很低,函数名超级长)。

JNINativeMethod

这是一个结构体,在 jni.h 头文件中定义:

typedef struct {const char* name;const char* signature;void* fnPtr;} JNINativeMethod;

Java 与 jni 通过该结构体建立联系,其中有三个变量:

  • name:Java 中函数的名字。
  • signature:签名符号,描述了函数的参数和返回值
  • fnPtr:函数指针,指向一个被调用的函数

我们看上面定义的结构体数组:

JNINativeMethod nativeMethod[] = {{"dynamicRegFromJni", "()Ljava/lang/String;", (void*)nativeDynamicRegFromJni}};

可以看出,里面有一个成员,该成员第一个参数 “dynamicRegFromJni”,java 函数名;第二个参数“()Ljava/lang/String:”,是签名符号,意思是该函数没有参数,返回一个字符串;第三个参数就是要调用的 native 方法。

关于 签名符号 可以参考这里:Android JNI编程—JNI基础

当 java 通过 System.loadLibrary 加载完 JNI 动态库后,紧接着会调用 JNI_OnLoad 的函数。

JNI_OnLoad()函数

JNI_OnLoad()函数有两个重要的作用:

1 指定 jni 版本:告诉 JVM 该组件使用哪一个 jni 版本(若未提供JNI_OnLoad()函数,JVM 会默认该使用最老的 JNI 1.1版本),如果要使用新版本的JNI,例如JNI 1.4版,则必须由 JNI_OnLoad() 函数返回常量 JNI_VERSION_1_4 (该常量定义在 jni.h 中) 来告知 JVM 。

2 一系列初始化操作,当 JVM 执行到 System.loadLibrary() 函数时,会立即调用 JNI_OnLoad() 方法,因此在该方法中进行各种资源的初始化操作最为恰当,

RegisterNatives

动态注册的工作就是在这里完成的。

RegisterNatives在 jni.h 中是这么定义的:

jint RegisterNatives(jclass clazz, const JNINativeMethod* methods,jint nMethods)

该函数有三个参数:

  • clazz: java 类名,通过 FindClass 得到
  • methods:JNINativeMethod 结构体指针
  • nMethods: 方法个数

我们看上面该函数的调用:

(*env) -> RegisterNatives(env, clz, nativeMethod, sizeof(nativeMethod) / sizeof(nativeMethod[0]));

咦,里面怎么又四个参数,嗯,是的,这个是 C 编写规则,如果是 C++ 的话,是这样的:

env -> RegisterNatives(clz, nativeMethod, sizeof(nativeMethod) / sizeof(nativeMethod[0]));

看出差别了吗,如果你遇到下面类似错误:

error: request for member 'FindClass' in something not a structure or union

可能就是 C 和 C++ 编写规则的问题咯,check 一下,没什么大问题的。

ok,这样就完成了 native 方法的动态注册

这里补充一点,jni 开发中 80% 会发生的错误:

Native method not found

比如我今天遇到了很多次这个错误:

UnsatisfiedLinkError: Native method not found: github.jp1017.hellojni.MainActivity.dynamicRegFromJni:()Ljava/lang/String;

该问题的发生原因很多,我这里报错是因为自己粗心,代码 大小写 写错,类名 写错等,所以这里提醒大家对待你的代码要像你的女朋友一样,千万不可马虎大意呀。

总结

上面的代码我给上传到了 github: https://github.com/jp1017/HelloJni

jni 开发告一段落了,这几天总结了很多坑,详情请点击这里:

Android Studio 下 jni 开发填坑记

安卓开源库收集整理中,欢迎 PR, star,地址:https://github.com/XXApple/AndroidLibs

分享是一种美德,更是一种生活方式!!

也许你会说我是一个梦想者,但我不是唯一的一个。

悦分享,越快乐^_^

欢迎交流,转载请注明出处,谢谢!

安卓 jni 开发之 native 方法的动态注册相关推荐

  1. android中oncreate方法,android开发之onCreate( )方法详解

    这里我们只关注一句话:This is where you should do all of your normal static set up.其中我们只关注normal static, normal ...

  2. android开发之onCreate( )方法详解

    android开发之onCreate( )方法详解 onCreate( )方法是android应用程序中最常见的方法之一,那么,我们在使用onCreate()方法的时候应该注意哪些问题呢? 先看看Go ...

  3. JNI开发之-Android.mk和Application.mk 详解

    Android.mk和Application.mk 详解 Android.mk 基础知识 变量和宏 NDK 定义的 include 变量 目标信息变量 模块描述变量 NDK 提供的函数宏 Applic ...

  4. Unity3D游戏开发之Unity3D中的动态阴影

    http://blog.csdn.net/qinyuanpei/article/details/32355267 博主今天想和大家分享的是Unity3D中的动态阴影,昨天博主重温了一下自己喜欢的游戏& ...

  5. edtext 从右边开始输入 安卓_Android开发之EditText属性详解

    1.EditText输入的文字为密码形式的设置 (1)通过.xml里设置: 把该EditText设为:android:password="true" // 以".&quo ...

  6. [Unity3D]Unity3D游戏开发之Unity3D中的动态阴影

    大家好,我是秦元培,欢迎大家关注我的博客,我的博客地址是blog.csdn.net/qinyuanpei. 博主今天想和大家分享的是Unity3D中的动态阴影,昨天博主重温了一下自己喜欢的游戏< ...

  7. JNI查找 native 方法的规则(静态、动态注册)

    转载自:JNI查找 native 方法的规则 通过上一篇文章,大家明白了调用 native 方法之前,首先要调用 System.loadLibrary 接口加载一个实现了native 方法的动态库才能 ...

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

    文章目录 I . 动态注册流程 ( 总结 ) II . JNI_OnLoad 方法 III . 被注册的本地 C/C++ 方法参数 IV . JNINativeMethod 结构体 ( 核心重点 ) ...

  9. android的动态注册,Android应用开发之BroadcastReceiver(广播)的静态注册和动态注册 --Android开发...

    本文将带你了解Android应用开发之BroadcastReceiver(广播)的静态注册和动态注册 --Android开发,希望本文对大家学Android有所帮助 BroadcastReceiver ...

最新文章

  1. github删除文件_github 仓库中删除历史大文件
  2. python3 set_python3.x 基础三:set集合
  3. Qt C++ 命名空间namespaces讲解
  4. codeigniter 辅助函数 - 敏感词过滤
  5. 字体乱码的时候,可以使用英文下的写法
  6. 沙盘正在注销进程start_电脑关不了机,一直处于正在注销,这是为什么
  7. centos7下给bond网卡配置bridge桥接
  8. python学习笔记16--javascript总结
  9. 【优化算法】变色龙算法(CSA)【含Matlab源码 1796期】
  10. 23.3.3 Web存储机制【JavaScript高级程序设计第三版】
  11. “数据打通”不等于“数据共融”,智能数据营销解决方案了解一下
  12. 《投射技术》与科学研究汉字笔迹心理学的希望
  13. autojs左右滑动脚本代码_Swiper.js实现移动端元素左右滑动
  14. 主码、候选码与外码的区别
  15. 偏差-方差权衡(bias-variance-tradeoff)
  16. PHOENIX IO模块配置
  17. 第二篇:Haploview做单倍型教程2--分析教程
  18. 2021年中国人工智能企业数量、投资数量及金额分析:国内互联网巨头腾讯企业投资达82家[图]
  19. Linux 如何设置代理
  20. 堡垒机拓扑图_fanzhenlong/堡垒机部署方案总结.md at master · leadsino/fanzhenlong · GitHub...

热门文章

  1. 如何读取md文件(MarkdownPad2专业版已激活)
  2. Adblock Plus使用教程
  3. 吕梁市服务器维修,终端服务器 吕梁知名智能车检网络摄像机 电话交通技术监控机柜...
  4. 四、Linux磁盘与文件系统管理
  5. 腾讯游戏助手运行闪退日志查看
  6. 点成分享| 低温冷冻保存的技术研究(一)
  7. 视频显示相关名词解释
  8. Deen Smart隐私政策
  9. 厉害了!那个用文言文编程的小哥,竟从28万行唐诗中找出了对称矩阵
  10. 你想要的资源我都有(2)