JNI的静态调用与动态注册
- JNI的定义
- JAVA调用JNI
- JNI回调JAVA方法
- 变量定义
JNI的定义
JNI是Java Native Interface的缩写,它提供了若干的API实现了Java和其他语言的通信(主要是C跟C++)。Android系统中主要用于SystemServer的各种JAVA服务调用本地服务。
JAVA调用JNI
java调用JNI方法,都必须以带native关键字的方式定义在一个java文件中,JNI回调JAVA方法,则不需要带native关键字。
public class JniTest {
//本地库编译的全名为libjnitest.so, loadLibrary方法只需加载库的名称
static{
System.loadLibrary("jnitest");
}
//另外还有一种库的加载方式,这种方式加载库需要使用库的绝对路径,
//比如在Android系统中,需要加载系统中的库,则:
//System.load("/system/lib/libxxx.so");
//定义本地方法
public native void set(int a);
public native int get();
//定义JNI回调Java的方法
public void callByJNI();
//定义JNI回调带int参数的java方法
public void callByJNI(int i);
}
- 静态注册
静态注册中java的native方法与C/C++代码函数是通过Java_<包名><类名><方法名>这种方式对应的
使用静态注册实现上述java文件中的本地方法如下:
#include <jni.h>
int value;
void Java_com_test_jni_JniTest_set(JNIEnv* env, jobject thiz, jint a){
value = a;
}
jint Java_com_test_jni_JniTest_get(JNIEnv* env, jobject thiz){
return value;
}
- 动态注册
与静态注册不同的是,动态注册不受命名的限制。动态注册分为三步:
1.定义本地方法与java方法对应的表,这里JNI提供了一个结构体JNINativeMethod来对应表。
2.JNI_OnLoad方法触发:当java类调用System.loadLibrary或者System.load时触发JNI的此方法
3.注册本地方法,使用JNIEnv结构体指针中的RegisterNatives方法来注册本地函数。
#include <jni.h>
#include <string>
static int mValue = 0;
void native_set(JNIEnv* env,jobject thiz,int value){
mValue = value;
}
jint native_get(JNIEnv* env,jobject thiz){
return mValue;
}
static JNINativeMethod method_tables[] = {
{"set", "(I)V", (void *)native_set}, //{java中的native方法名,参数类型,JNI方法}
{"get", "()I", (void *)native_get},
};
static int registerNativeMethods(JNIEnv* env,
const char* className,
JNINativeMethod* jMethods,
int numMethods) {
jclass clazz;
clazz = env->FindClass(className);
if (clazz == NULL) {
return JNI_FALSE;
}
if (env->RegisterNatives(clazz, jMethods, numMethods) < 0) {
return JNI_FALSE;
}
return JNI_TRUE;
}
static int registerNatives(JNIEnv* env) {
const char* jClassName = "com/test/jnitest/JniTest";//指定要注册的类
return registerNativeMethods(env, jClassName, method_tables, sizeof(method_tables) / sizeof(method_tables[0]));
}
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env = NULL;
jint ret = -1;
if (vm->GetEnv((void **)&env, JNI_VERSION_1_4) != JNI_OK) {
goto fail;
}
assert(env != NULL);
if (!registerNatives(env)) {//注册
goto fail;
}
ret = JNI_VERSION_1_4;
return ret;
fail:
return -1;
}
JNI回调JAVA方法
JNI回调JAVA方法,类似JAVA的类的反射调用方式, 接上述动态注册的例子,步骤如下:
1. 通过JNIEnv结构体指针找到对应的java class对象
2. 通过jclass对象找到class中对应方法的methodID
3. 根据方法理性,使用不同的JNI方法回调函数。
/*
* 此处省略动态注册例子中其他代码...
*/
jint native_get(JNIEnv* env, jobject thiz){
//回调不带参数的java方法
jclass j_class = env->FindClass(env,"com/test/jni/JniTest");
jmethodID method = env->GetMethodID(env, j_class, "callByJNI","()V");
env->CallVoidMethod(env, thiz, method);
//回调带参数的java方法
//jclass j_class = env->FindClass(env,"com/test/jni/JniTest");
//jmethodID method = env->GetMethodID(env, j_class, "callByJNI","(I)V");
//env->CallVoidMethod(env, thiz, method, 666);
return value
}
变量定义
Java基本数据类型对应JNI数据类型:
Java类型 | 本地C类型 |
---|---|
boolean | jboolean |
byte | jbyte |
char | jchar |
short | jshort |
int | jint |
long | jlong |
float | jfloat |
double | jdouble |
void | void |
参数类型对应:
描述符 | JNI类型 | JAVA类型 |
---|---|---|
V | void | void |
Z | jboolean | boolean |
I | jint | int |
J | jlong | long |
D | jdouble | double |
F | jfloat | float |
B | jbyte | byte |
C | jchar | char |
S | jshort | short |
[I | jintArray | int[] |
[F | jfloatArray | float[] |
[B | jbyteArray | byte[] |
[C | jcharArray | char[] |
[S | jshortArray | short[] |
[D | jdoubleArray | double[] |
[J | jlongArray | long[] |
[Z | jbooleanArray | boolean[] |