java类sample是公共的_应在名samle.java的文件_Andoid NDK编程 1 - 注册native函数
打算对Android的NDK的开发做一总结,首先是JNI部分,接下来是NDK的内容。今天首先介绍一下JNI的第一部分:注册native函数。
当java代码中执行native的代码时候,首先是通过一定的方法来找到这些native方法。而注册native函数的具体方法的不同,会导致系统在运行时采用不同的方式来寻找这些native方法。
JNI有如下两种注册native方法的途径:静态和动态。其中:
静态:先由Java得到本地方法的声明,然后再通过JNI实现该声明方法。
动态:先通过JNI重载JNI_OnLoad()实现本地方法,然后直接在Java中调用本地方法。
静态注册
根据函数名找到对应的JNI函数:Java层调用函数时,会从对应的JNI中寻找该函数,如果没有就会报错,如果存在则会建立一个关联联系,以后在调用时会直接使用这个函数,这部分的操作由虚拟机完成。
静态方法就是根据函数名来遍历java和jni函数之间的关联,而且要求jni层函数的名字必须遵循
特定的格式。
具体的实现很简单,首先在java代码中声明native函数,然后通过javah来生成native函数的具体形式,接下来在JNI代码中实现这些函数即可。
看个例子就更明了了:
Java层:
static {
System.loadLibrary("samplelib_jni");
registerNatives();
}
private native void nativeFunc1();
private native void nativeFunc2();
private native void nativeFunc3();
接下来通过javah来产生jni代码声明:
假设你的java文件的包名是com.jni.samle 而类名是JniSample。
javah -d ./jni/ -classpath /Users/YOUR_NAME/Library/Android/sdk/platforms/android-21/android.jar:../../build/intermediates/classes/debug/ com.jni.samle.JniSample
然后就会得到一个JNI的.h文件,里面包含这几个native函数的声明,观察一下文件名以及函数名,会有一定的规律,我会在下一篇文章中对此做一详细介绍,在此不再赘述。
最后实现jni层的native方法即可。
动态注册
对Java程序员来说,可能我们总是会遵循:1.编写带有native方法的Java类;—->2.使用javah命令生成.h头文件;—->3.编写代码实现头文件中的方法,这样的标准流程,但也许有人无法忍受那“丑陋”的方法名称,所以我们可以采用动态注册方法,也就是通过RegisterNatives方法把c/c++中的方法隐射到Java中的native方法,而无需遵循特定的方法命名格式。
JNI 允许你提供一个函数映射表,注册给Jave虚拟机,这样Jvm就可以用函数映射表来调用相应的函数,
就可以不必通过函数名来查找需要调用的函数了。
Java与JNI通过JNINativeMethod的结构来建立联系,它在jni.h中被定义,其结构内容如下:
typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;
第一个变量name是Java中函数的名字。
第二个变量signature,用字符串是描述了函数的参数和返回值
第三个变量fnPtr是函数指针,指向C函数。
当java通过System.loadLibrary加载完JNI动态库后,紧接着会查找一个JNI_OnLoad的函数,如果有,就调用它, 而动态注册的工作就是在这里完成的。
一起来看一下具体的实现方法:
Java code:
比较简单,仅仅是加载so库。
static {
System.loadLibrary("samplelib_jni");
}
JNI code:
在JNI中实现
jint JNI_OnLoad(JavaVM* vm, void* reserved)
并且在这个函数里面去动态的注册native方法,完整的参考代码如下:
#include
#include "Log4Android.h"
#include
#include
using namespace std;
#ifdef __cplusplus
extern "C" {
#endif
static const char *className = "com/zhixin/jnisample/JniManager";
static void sayHello(JNIEnv *env, jobject, jlong handle) {
LOGI("JNI", "native: say hello ###");
}
static JNINativeMethod gJni_Methods_table[] = {
{"sayHello", "(J)V", (void*)sayHello},
};
static int jniRegisterNativeMethods(JNIEnv* env, const char* className,
const JNINativeMethod* gMethods, int numMethods)
{
jclass clazz;
LOGI("JNI","Registering %s natives\n", className);
clazz = (env)->FindClass( className);
if (clazz == NULL) {
LOGE("JNI","Native registration unable to find class '%s'\n", className);
return -1;
}
int result = 0;
if ((env)->RegisterNatives(clazz, gJni_Methods_table, numMethods) < 0) {
LOGE("JNI","RegisterNatives failed for '%s'\n", className);
result = -1;
}
(env)->DeleteLocalRef(clazz);
return result;
}
jint JNI_OnLoad(JavaVM* vm, void* reserved){
LOGI("JNI", "enter jni_onload");
JNIEnv* env = NULL;
jint result = -1;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
return result;
}
jniRegisterNativeMethods(env, className, gJni_Methods_table, sizeof(gJni_Methods_table) / sizeof(JNINativeMethod));
return JNI_VERSION_1_4;
}
#ifdef __cplusplus
}
#endif
比较
下面我们来比较一下这两种不同的实现方法。
首先是静态方法:
优点实现起来比较简单,直接用javah就能将Java代码中的native函数的声明转化为native代码的函数。
缺点在于:javah生成的jni层函数特别长;
初次调用native函数时要根据名字搜索对应的jni层函数来建立关联联系,这样影响效率。
而动态方法的优缺点刚好和静态方法相反。
通过上面的介绍我们发现,尽管静态注册实现起来比较简单,但是会导致效率相对来说比较低。
所以在JNI层提供JNI_OnLoad是一个推荐的做法。如果不提供JNI_OnLoad,那么JNI函数的命名就需要符合规范,并且需要导出该函数。这样做很容易被别人反编译。相反,如果提供JNI_OnLoad,就可以在里面自己注册JNI函数给Dalvik VM,这样JNI函数的命名就可以按照自己的习惯了,而且也不用导出。
一个巧妙的合作
在实际的应用中,我们可以巧妙的将静态注册和动态注册结合起来:也就是在java代码中仍然声明一个native函数,但是这个函数仅仅是用来去触发在JNI层的native函数的动态注册。说的有些绕,看看代码就明白了:
Java层:
static {
System.loadLibrary("samplelib_jni");
registerNatives();
}
private static native void registerNatives();
JNI层:
通过javah生成java层声明的native函数的文件,并且在实现代码中去动态注册JNI函数:
JNIEXPORT void JNICALL Java_com_zhixin_jni_JniSample_registerNatives
(JNIEnv *env, jclass clazz){
(env)->RegisterNatives(clazz, gJni_Methods_table, sizeof(gJni_Methods_table) / sizeof(JNINativeMethod));
}
后续
关于JNI的函数注册就到这里了,在下一篇文章中会总结JNI的签名规则问题。
java类sample是公共的_应在名samle.java的文件_Andoid NDK编程 1 - 注册native函数相关推荐
- java类中声明log对象_用于Android环境,java环境的log打印,可打印任何类型数据
LogXixi 用于Android环境,java环境的log打印,可打印任何类型数据,根据android项目环境debug环境自动打开,release环境自动关闭android环境log打印,规范be ...
- java类二次加载_深入理解java之类加载器
一.类与类加载器 类加载器:实现加载阶段的第一步,通过一个类的全限定名来将这个类的二进制字节流加载进jvm. 类与类加载器:任意一个类唯一性都是由它本身和加载它的类加载器确定,两个类是否相等在它们是由 ...
- java获取当月有几天_你真的能在JAVA开发这条路上面一直坚持下去吗?
JAVA为什么有前途? 过去的十多年,JAVA基本每年都是全世界使用人数第一的语言.全世界数百万的IT企业构建了庞大的JAVA生态圈,大量的软件基于JAVA开发. JAVA也被誉为"计算机界 ...
- java洗衣店管理课程设计报告_课内资源 - 基于Java的洗衣店管理系统
摘 要 随着科技的快速发展,人们的需求也是越来越多,为了方便对信息的管理我们小组就设计了一个洗衣店订单的管理系统. 洗衣店管理系统是典型的的信息管理系统,创建了六个类,分别是:Test类:Person ...
- java教学视频毕向东_集合3--毕向东java基础教程视频学习笔记
Day 15 集合框架 01 TreeSet 02 TreeSet存储自定义对象 03 二叉树 04 实现Comparator方式排序 05 TreeSet练习 06 泛型概述 07 泛型使用 08 ...
- java集合到线程的考试_成都汇智动力-Java SE考试编程题总结
原标题:成都汇智动力-Java SE考试编程题总结 线程和进程的区别: (1)进程是运行中的程序,拥有自己独立的内存空间和资源; (2)一个进程可以有一个或多个线程组成,且至少有一个线程称为主线程; ...
- java坦克大战 实训报告_坦克大战系统《Java程序开发实训》综合实训报告.doc
坦克大战系统<Java程序开发实训>综合实训报告 <Java程序开发实训>综合实训报告 题目: 坦克大战系统 姓名: 方庆 学号: 2010203206 班级: 10软件(2) ...
- Java好还是网优好_大神告诉你|Java好还是Python好?
在进入主题之前,小智先分享几条大神的人生警句.醒世明言: 初学者才争论语言,我们只看心情. 还有: 选择入门语言最重要的是 "三观"相似 "臭味"相投 以及大家 ...
- java中build是什么意思_科普 eclipse中的Java build
在刚学eclipse的时候,build path是经常会用到的,但经常就是跟着教程走,额就不太懂这是干嘛的,然后今天看见极客视频里有相关的讲解,来记录一下. Build Path 是指定Java工程所 ...
最新文章
- CoordinatorLayout 的jar包位置
- 你是怎样给下属分配工作
- mysql报错:Reading table information for completion of table and column names
- java音乐播放器脚本之家,分享|3 个开源的音乐播放器:Aqulung、Lollypop 和 GogglesMM...
- 五、Netty核心组件
- eclipse中YAML文件编辑插件:Yaml Editor插件安装
- Objections vs. excuses
- NBA Top Shot巨鲸17.5万美元购买的球星卡目前已值2000万美元
- 零基础学SQL(一、数据库与SQL简介)
- 利用图神经网络进行的知识图谱补全综述
- android 反编译 .smali,Android_反编译_smali语法
- python原生是什么意思_什么是 云原生?
- c#窗体编辑个人简历_3年工作经验.net程序员简历应该怎么写
- java mysql物联网土壤智能监控web前端+java后台+数据接程序
- 用自己拍的一张照片做海报
- 【SAP-CO】CO模块主要子模块相关概念
- NanoHttp的使用入门
- pythonsuper继承规则,Python用super继承
- 《上古天真论》第四讲文字版
- NoSql、MongoDb 数据库介绍及MongoDb安装、使用