建设培训中心网站/百度快照推广是什么意思
1 概述
JNI不是Android特有的,是通用的。用于Java与C++(C)层代码的相互调用。
Java是解释型语言,虽然JVM经过了很多优化,性能大大提高,但是始终比不上C++这种编译型语言。
从Android架构来看,framework层为Java语言编写,但是很多实际运行的却是在Native层(C\C++语言编写)。
2 应用层JNI开发
App应用开发时可以创建C++工程,会默认创建JNI调用关系
MainActivity
public class MainActivity extends AppCompatActivity {// Used to load the 'jnidemo' library on application startup.static {System.loadLibrary("jnidemo");}private ActivityMainBinding binding;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);binding = ActivityMainBinding.inflate(getLayoutInflater());setContentView(binding.getRoot());// Example of a call to a native methodTextView tv = binding.sampleText;tv.setText(stringFromJNI());}/*** A native method that is implemented by the 'jnidemo' native library,* which is packaged with this application.*/public native String stringFromJNI();
}
首先导入libjnidemo.so,这是C++代码编译后的产物
stringFromJNI方法由native关键字修饰,表示具体实现是C++函数,会调用C++函数。
native-lib.cpp
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_jnidemo_MainActivity_stringFromJNI(JNIEnv* env,jobject /* this */) {std::string hello = "Hello from C++";return env->NewStringUTF(hello.c_str());
}
默认C++工程会生成一个cpp文件,该文件的编译使用cmake。
文件中自动生成了一个函数,这个函数就是对应于stringFromJNI这个Java方法的C++函数。这里采用的是静态注册的方式使Java方法与C++函数产生对应关系。当在Java调用stringFromJNI方法时就会调用到C++中的Java_com_example_jnidemo_MainActivity_stringFromJNI函数。
3 JNI数据结构
jni拥有自己的数据结构,与Java和C++有对应关系,这个从jni.h的定义中就能推断出来
jni.h
/* Primitive types that match up with Java equivalents. */
typedef uint8_t jboolean; /* unsigned 8 bits */
typedef int8_t jbyte; /* signed 8 bits */
typedef uint16_t jchar; /* unsigned 16 bits */
typedef int16_t jshort; /* signed 16 bits */
typedef int32_t jint; /* signed 32 bits */
typedef int64_t jlong; /* signed 64 bits */
typedef float jfloat; /* 32-bit IEEE 754 */
typedef double jdouble; /* 64-bit IEEE 754 *//* "cardinal indices and sizes" */
typedef jint jsize;
基本数据类型的定义,与Java的基本数据类型对应
typedef _jobject* jobject;
typedef _jclass* jclass;
typedef _jstring* jstring;
typedef _jarray* jarray;
typedef _jobjectArray* jobjectArray;
typedef _jbooleanArray* jbooleanArray;
typedef _jbyteArray* jbyteArray;
typedef _jcharArray* jcharArray;
typedef _jshortArray* jshortArray;
typedef _jintArray* jintArray;
typedef _jlongArray* jlongArray;
typedef _jfloatArray* jfloatArray;
typedef _jdoubleArray* jdoubleArray;
typedef _jthrowable* jthrowable;
typedef _jobject* jweak;
对象的定义,对应于Java中的引用。其中jobject对应于Java中的实例对象,jclass对应于Java中的Class。其他就对应于Java中各种数组。
3 JNI注册
3.1 静态注册
静态注册通过java方法名来查找so中标志有JNIEXPORT和JNICALL宏定义的特定名称的函数
以上面的为例stringFromJNI对应于Java_com_example_jnidemo_MainActivity_stringFromJNI,可以看出C++对应函数命名为:Java_包名(.替换成_)_类名_方法名
3.2 动态注册
JNI的动态注册依赖方法签名,每一个Java函数都有其特定的方法签名,使用javap -s可以查看字节码文件中方法签名
对于以下类
package com.example.jnidemo;public class Test {int a;int b;public String test(){return "test";}public native String stringFromJNI();public native String add(int a, int b);
}
javac编译成class文件之后,执行
javap -s .\Test.class
会输出方法签名
public class com.example.jnidemo.Test {int a;descriptor: Iint b;descriptor: Ipublic com.example.jnidemo.Test();descriptor: ()Vpublic java.lang.String test();descriptor: ()Ljava/lang/String;public native java.lang.String stringFromJNI();descriptor: ()Ljava/lang/String;public native java.lang.String add(int, int);descriptor: (II)Ljava/lang/String;
}
类似(II)Ljava/lang/String就是add方法的签名,包含了两个int值的参数,所以括号里面是两个I,后面是返回值类型。
jstring Add(JNIEnv *env, jobject object, jint a, jint b)
{printf("Add");return env->NewStringUTF("Add");
}JNINativeMethod getNativeMethods[]={{"add", "(II)Ljava/lang/String;",(void*)Add},
};
int register_android_method(JNIEnv *env) {jclass clazz = env->FindClass("com/example/jnidemo/MainActivity");if (clazz == nullptr) {return JNI_ERR;}return env->RegisterNatives(clazz, getNativeMethods, sizeof(getNativeMethods)/sizeof(JNINativeMethod));
}extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
{JNIEnv* env = NULL;(void)reserved;if(vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {return JNI_ERR;}if(NULL == env) {printf("JNI_OnLoad env is null.");return JNI_ERR;}if (register_android_method(env) < 0) {printf("register method fail.");return JNI_ERR;}return JNI_VERSION_1_4;
}
当System.loadLibrary加载so时,会查找so中的JNI_OnLoad并执行。然后调用register_android_method中的RegisterNatives完成jni的动态注册,完成了java方法与JNI函数的映射关系。