Android中关于JNI 的学习(六)JNI中注冊方法的实现
在前面的样例中,我们会发现,当在Java类中定义一个方法的时候,例如以下:
public class ParamTransferTest {
public static int testval = 1;
public native void changeTestVal();
则在相应的JNI层中,由javah生成的头文件和其相应的C文件,其方法名称必须例如以下:
JNIEXPORT void JNICALL Java_com_lms_jni_ParamTransferTest_changeTestVal
而方法名称之所以必须是这样,是基于在前面文章中我们提到过的一些JNI的命名规则,可是是不是一定要这样才行呢,多麻烦呀。
(tips:JNIExport 和 JNICALL是windows平台的jni编译出来的,在Android手机上,事实上能够不要这两个keyword的)
答案当然是否定的。
在前面的样例中,为什么在JNI层实现的方法名必须符合一定的命名规则呢?这是由于,我们并没有提供JNI层方法和Java端方法的一个联系,或者说一个相应关系,而由于缺乏这样的我们自己定义的相应关系,NDK在编译的时候,操作系统在解释的时候,它们就必须制定一系列的规则,而通过这个规则去找到相应的方法。不然,假设你随便定义一个方法名,我随便定义一个方法名,别人怎么可能知道这两个就是相应的呢?无规则不成方圆,所以。。。
那么,假设我们不想再去写这些又长又臭的方法,我们就必须给出它们的相应关系,而JNI事实上也提供了这种一套机制,这篇博文就让我们来看看,怎样在Android中实现这种机制。
事实上Android底层的源代码中,涉及到JNI层的方法实现和载入,基本上都是通过这样的注冊方法的机制来实现的,包括我们上一篇文章中提到的Log的实现。
在jni.h文件里,提供了JNINativeMethod的结构,例如以下:
typedef struct {
const char* name;<span style="white-space:pre"> </span>//java端方法名
const char* signature;<span style="white-space:pre"> </span>//方法签名
void* fnPtr;<span style="white-space:pre"> </span>//jni层函数指针
} JNINativeMethod;
JNINativeMethod这个结构,存放的就是Java端方法跟JNI层方法的一个相应关系,其有三个字段,表示的意思,大家看凝视就清楚了。
接下来,我们通过一个小Demo来看看怎样在JNI中实现注冊函数,然后由DVM载入使用。
1)在Java端定义一个Native方法,例如以下:
public class DynReg {
public native static String sayHello();
}
2)在JNI层中创建相应的C文件,这一次,我们不须要生成利用javah生成头文件之类,然后复制方法名之类的,例如以下:
jstring say_hello(JNIEnv *e, jobject j) {
return (*e)->NewStringUTF(e, "Say Hello from dynamic register");
}
我们并不须要遵循传统的JNI编程命名方法,我们能够自定义我们想要的方法名称,比方say_hello等等。
4)利用JNINativeMethod结构,创建一个相应的关系,例如以下:
static JNINativeMethod mehtod_table[] = {
{ "sayHello", "()Ljava/lang/String;", (void*) say_hello },
};
这是一个结构数组,相应于多个方法,在这个样例中,我们仅仅有一个方法,所以仅仅有一个元素,我们能够看到:
4.1)“sayHello”,相应于Java端的方法
4.2)"()Ljava/lang/String",相应于其方法签名
4.3)(void*) say_hello,相应于我们上面实现的C方法
5)在JNI_OnLoad方法中将这个相应注冊到DVM中,例如以下:
static int registerNativeMethods(JNIEnv* env, const char* className,
JNINativeMethod* methods, int numMethods) {
jclass clazz;
clazz = (*env)->FindClass(env, className);
if (clazz == NULL) {
return JNI_FALSE;
}
if ((*env)->RegisterNatives(env, clazz, methods, numMethods) < 0) {
return JNI_FALSE;
} return JNI_TRUE;
} jint JNI_OnLoad(JavaVM* jvm, void* reserved) {
JNIEnv* env = NULL;
jint result = -1;
if ((*jvm)->GetEnv(jvm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
return result;
} registerNativeMethods(env, "com/lms/jni/DynReg", mehtod_table, NELEM(mehtod_table)); return JNI_VERSION_1_4;
}
简单讲一下这个过程,当这些C文件被编译成相应的so文件之后,在Java端中利用System.loadLibrary来载入so库的时候,相应类中的JNI_OnLoad方法就会被调用,这事实上就是相当于一些接口的回调函数的概念, 在载入的时候,我们就能够将上面定义的JNINativeMethod数组,通过调用JNIEnv*的RegisterNatives方法,将这个数组中的方法给注冊到JNIEnv中了,而在底层的实现中,当DVM调用相应的native方法的时候,会依据我们这里有没有注冊到JNIEnv*中去找相应的方法,假设找到了,就直接运行,假设没有找到,JNIEnv*就会继续依据传统的JNI方法命名规范去找原先那又长又臭的方法,假设那个方法也没有找到,就会报错了。
5.1)调用registerNativeMethods,传入相应的类名“com/lms/jni/DynReg”,还有方法表method_table,方法表中方法的个数,NELEM是定义的一个宏,例如以下:
# define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
5.2)在registerNativeMethods方法中,依据类名,找出相应的类,将类,方法和方法个数调用RegisterNatives方法注冊到JNIEnv*中。
6)在Android.mk文件里声明我们这个新加入�的类 DynReg.c,例如以下:
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := com_lms_jni_HwDemo LOCAL_SRC_FILES := \
HwDemo.c \
JniTest.c \
ParamTransferTest.c\
DynReg.c LOCAL_LDLIBS += -llog include $(BUILD_SHARED_LIBRARY)
7)最后在Java端中使用这种方法,例如以下:
tv.setText(DynReg.sayHello());
8)结果例如以下:
关于在JNI中注冊函数实现JNI层和Java层互相通信的方法到这里也就差点儿相同了,利用这种方法,在编写C/C++方法的时候,是不是看起来就舒服多了啊?
结束。
Android中关于JNI 的学习(六)JNI中注冊方法的实现的更多相关文章
- Android中的SQLite使用学习
Android中的SQLite使用学习 SQLite是非常流行的嵌入式关系型数据库,轻载, 速度快,而且是开源.在Android中,runtime提供SQLite,所以我们可以使用SQLite,而且是 ...
- Android学习记录(3)—Android中ContentProvider的基本原理学习总结
一.ContentProvider简介 当应用继承ContentProvider类,并重写该类用于提供数据和存储数据的方法,就可以向其他应用共享其数据.虽然使用其他方法也可以对外共享数据 ...
- android中的线程池学习笔记
阅读书籍: Android开发艺术探索 Android开发进阶从小工到专家 对线程池原理的简单理解: 创建多个线程并且进行管理,提交的任务会被线程池指派给其中的线程进行执行,通过线程池的统一调度和管理 ...
- Android 中onSaveInstanceState和onRestoreInstanceState学习
1. 基本作用: Activity的 onSaveInstanceState() 和 onRestoreInstanceState()并不是生命周期方法,它们不同于 onCreate().onPaus ...
- Android 中Thread,Handler,Loop学习
1.先看一下最简单的进度条示例 EG: package com.sxz.android.thread; import java.util.concurrent.atomic.AtomicBoolean ...
- 在Android中使App高速、简单地支持新浪微博、微信、QQ、facebook等十几个主流社交平台的分享功能
前言 在如今的APP或者游戏中,分享功能差点儿已经成为标配.分享功能不但能够满足用户的需求.也能够为产品带来很多其它的用户,甚至能够对用户的行为.活跃度.年龄段等情况进行数据统计,使得软件公司能够对产 ...
- android学习笔记----JNI中的c控制java
面向对象的底层实现 java作为面向对象高级语言,可对现实世界进行建模.和面向过程不同的是面向对象软件的编写不是流程的堆积,而是对业务逻辑的多视角分解和分类.其过程大致为: 1).将知识分解 ...
- Android中关于JNI 的学习(三)在JNI层訪问Java端对象
前面两篇文章简介了JNI层跟Java层的一些相应关系,包含方法名,数据类型和方法名称等,相信在理论层面.可以非常好地帮助我们去了解JNI在Native本地开发中的作用,对JNI的一些概念也有了一个初步 ...
- JNI学习笔记_Java调用C —— 非Android中使用的方法
一.学习笔记 1.java源码中的JNI函数本机方法声明必须使用native修饰. 2.相对反编译 Java 的 class 字节码文件来说,反汇编.so动态库来分析程序的逻辑要复杂得多,为了应用的安 ...
随机推荐
- 3890: [Usaco2015 Jan]Meeting Time( dp )
简单的拓扑图dp.. A(i, j), B(i, j) 表示从点 i 长度为 j 的两种路径是否存在. 用bitset就行了 时间复杂度O(m) --------------------------- ...
- iOS - 单例传值 (一)
点击打开链接 iOS - 单例传值 (二) 单例只会对某个类实例化一次/单例类,对单例这个类实例化一次有且仅有一个对象 你单例初始化,只能初始化一次,然后你指向的对象,其实都是指向一个内存地址, ...
- Internet基础
互联网是什么? Internet是一个互联网,它是将提供不同服务的,使用不同技术的,具有不同功能的物理网络互连起来而形成的. TCP/IP是一个协议集,它对Internet中主机的寻址方式,主机的命名 ...
- hdu4708 Rotation Lock Puzzle
Rotation Lock Puzzle Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others ...
- response.sendRedirect("")和request.getRequestDispatcher("").forward(req,resp);
1:request.getRequestDispatcher("转发路径").forward(req,resp)该语句是实现请求转发的,当请求进入到该servlet中执行到该语句时 ...
- 有N个正实数(注意是实数,大小升序排列) x1 , x2 ... xN,另有一个实数M。 需要选出若干个x,使这几个x的和与 M 最接近。 请描述实现算法,并指出算法复杂度
题目:有N个正实数(注意是实数,大小升序排列) x1 , x2 ... xN,另有一个实数M. 需要选出若干个x,使这几个x的和与 M 最接近. 请描述实现算法,并指出算法复杂度. 代码如下: #in ...
- 硬盘被误格式化或Ghost还原后的数据恢复
硬盘格式化(Ghost还原)后的数据恢复 ---diskgenius使用之数据恢复 问题引出:计算机中病毒后用Ghost版本的winxp安装,由于安装途中选择了把映像安装到硬盘而不是分区,安装好后只剩 ...
- MongoDB shell操作
shell命令操作语法和JavaScript很类似,其实控制台底层的查询语句都是用JavaScript脚本完成操作的.使用shell 命令,需要启动mongo.exe. 常用shell命令如下: 1. ...
- hdu1087Super Jumping! Jumping! Jumping!(最大递增序列和)
题意:棋牌游戏如今,一种被称为“超级跳!跳!跳!“HDU是非常流行的.也许你是个好孩子,这个游戏知之甚少,所以我介绍给你吧. 可以玩游戏由两个或两个以上的球员 .它由一个棋盘(棋盘)和一些棋子(棋子) ...
- 终于懂了:TWinControl.DefaultHandler里的CallWindowProc(FDefWndProc)还挺有深意的,TButton对WM_PAINT消息的处理就是靠它来处理的(以前不明白为什么总是要调用inherited,其实就是没有明白TWinControl.DefaultHandler的真正用处)
我忽然发现:TButton既没有处理WM_PAINT,又没有Paint()或者PaintWindow(),那么它是什么时候被绘制的? Form1上放2个TButton,然后设置代码: procedur ...