原文链接:https://blog.csdn.net/q610098308/article/details/79395232
1.JNI概述
Android系统按语言来划分的话由两个世界组成,分别是Java世界和Native世界。那为什么要这么划分呢?Android系统由Java写不好吗?除了性能的之外,最主要的原因就是在Java诞生之前,就有很多程序和库都是由Native语言写的,因此,重复利用这些Native语言编写的库是十分必要的,况且Native语言编写的库具有更好的性能。
这样就产生了一个问题,Java世界的代码要怎么使用Native世界的代码呢,这就需要一个桥梁来将它们连接在一起,而JNI就是这个桥梁。
通过JNI,Java世界的代码就可以访问Native世界的代码,同样的,Native世界的代码也可以访问Java世界的代码。
静态注册
原理:根据函数名来建立 java 方法与 JNI 函数的一一对应关系;
实现流程:
编写 java 代码;
利用 javah 指令生成对应的 .h 文件;
对 .h 中的声明进行实现;
弊端:
编写不方便,JNI 方法名字必须遵循规则且名字很长;
编写过程步骤多,不方便;
程序运行效率低,因为初次调用native函数时需要根据根据函数名在JNI层中搜索对应的本地函数,然后建立对应关系,这个过程比较耗时;
动态注册
原理:利用 RegisterNatives 方法来注册 java 方法与 JNI 函数的一一对应关系;
实现流程:
利用结构体 JNINativeMethod 数组记录 java 方法与 JNI 函数的对应关系;
实现 JNI_OnLoad 方法,在加载动态库后,执行动态注册;
调用 FindClass 方法,获取 java 对象;
调用 RegisterNatives 方法,传入 java 对象,以及 JNINativeMethod 数组,以及注册数目完成注册;
优点:
流程更加清晰可控;
效率更高;
JNINativeMethod
在动态注册的过程中使用到了结构体 JNINativeMethod 用于记录 java 方法与 jni 函数的对应关系
结构体的第一个参数 name 是java 方法名;
第二个参数 signature 用于描述方法的参数与返回值;
第三个参数 fnPtr 是函数指针,指向 jni 函数;
其中,第二个参数 signature 使用字符串记录方法的参数与返回值,具体格式形如“()V”、“(II)V”,其中分为两部分,括号内表示的是参数,括号右侧表示的是返回值;
数据类型映射
2. 数组引用类型
如果是一维数组则遵循下表,如果是二维数组或更高维数组则对应的 native 类型为 jobjectArray,域描述符中使用 ‘[’ 的个数表示维数
3. 对象引用类型
对于其它引用类型,即 java 中的对象,其映射规则为
4. 对象数组引用类型
如果是一维数组则遵循下表,如果是二维数组或更高维数组则对应的 native 类型为 jobjectArray,域描述符中使用 ‘[’ 的个数表示维数
jni 函数默认参数
在 jni 函数中有两个默认参数
JNIEnv *env, jobject thiz
其中 JNIEnv 指代的是当前 java 环境,可以利用 JNIEnv 可以操作 java 层代码;jobject 指代的是 jni 函数对应的 java native 方法的类实例,如果 java 方法是 static,则代表的是 class 对象;
例:
java
package cn.com.jni.jniautoreg;
public class TestJNI {
public static native void native_init();
public static native String stringFromJNI(String name,String address);
}
native
#include <jni.h>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <android/log.h>
#include <memory.h>
#define LOG_TAG "my-jni"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG, __VA_ARGS__)
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,LOG_TAG, __VA_ARGS__)
#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
using namespace std;
static const char *const kClassJniTest =
"cn/com/jni/jniautoreg/TestJNI";
jstring JNI_stringFromJNI(JNIEnv *env, jobject obj, jstring name,jstring address) {
LOGE("JAVA call JNI stringFromJNI");
string hello = "Hello from JNI";
return env->NewStringUTF(hello.c_str());
}
void JNI_native_init(JNIEnv *env, jobject thizz) {
LOGE(" native_1init");
}
static const JNINativeMethod gMethods[] = {
{
"native_init",
"()V",
(void *) JNI_native_init
},
{
"stringFromJNI",
"(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
(void *) JNI_stringFromJNI
},
};
static int registerNativeMethods(JNIEnv *env, const char *className, const JNINativeMethod *gMethod,
int numMethods) {
jclass clazz;
clazz = env->FindClass(className);
if (clazz == NULL) {
LOGI(" JNI reg faild:%s",className);
return JNI_FALSE;
}
if (env->RegisterNatives(clazz, gMethod, numMethods) < 0) {
LOGI(" JNI reg method failed:%s",gMethod->name);
return JNI_FALSE;
}
return JNI_TRUE;
}
static int register_along_jni(JNIEnv *env) {
return registerNativeMethods(env, kClassJniTest, gMethods,
NELEM(gMethods));
}
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void * /* reserved */) {
JNIEnv *env = NULL;
jint result = -1;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
LOGE("ERROR: JNI version error");
return JNI_ERR;
}
if (register_along_jni(env) == -1) {
LOGE("ERROR: JNI_OnLoad failed");
return JNI_ERR;
}
result = JNI_VERSION_1_6;
return result;
}
注意:
在JNI_OnLoad函数的结尾处,我们一定要有返回值,而且必须是JNI_VERSION_1_4 或 JNI_VERSION_1_6,也就是JNI的版本号,我们一定要返回正确的版本号,否则系统也是无法加载的。
————————————————
版权声明:本文为CSDN博主「恋恋西风」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/q610098308/article/details/79395232