JNI调用流程

##1.Java调用C的过程

①Java先加载Library,使用System.LoadLibrary(“native”),一般放置在静态代码块中使其能在Class初始化的时候既被调用。
调用dlopen函数,打开一个so文件并创建一个handle;
调用dlsym()函数,查看相应so文件的JNI_OnLoad()函数指针,并执行相应函数。

②在加载库后会先调用C的JNI_Onload()函数,在这个函数里通过Jvm指针获取env,并通过env调用FindClass找到JClass变量,通过env的RegisterNatives建立C与Java之间的函数映射关系。

1
2
3
4
5
typedef struct{
char* name,//java方法名
char* signature, //JNI字段描述符
void *fnPtr //C函数名
}
1
2
3
4
5
6
7
8
JNI_OnLoad(JavaVM *jvm,Void *reservd){
(*jvm)->GEnv(jvm);
// 通过jvm获取当前java环境变量
jclazz=(*env)->FindClass(env,"Native");
// 获取调用的java类名
(*env)->RegisterNative(env,jclazz,methods,1);
// 建立C函数与java方法之间的映射关系,其中映射关系在methods这个存储了映射关系的结构体数组中
}

③Java 声明函数并调用
public native void hello();
public static native void hello();
当声明为Static直接调用hello,当声明为非Static应用当前加载库的类对象调用

*env相当于JVM的管家,通过它可以访问JVM中的各种对象和提供的方法

##2.C调用Java的过程

①创建JVM获取env,通过JNI_CreateJavaVM(JavaVM jvm,void argv)来创建JVM,其中返回值在传参内

②通过FindClass 加载类
jclass cls=(*env)->FindClass(env,"Native");
③调用静态方法先获取方法ID再调用

1
2
3
4
jmethodID mid;
mid=(*env)->GetStaticMethodID(env,cls,"main",signature);//main表示方法名

(*env)->CallStaticVoidMethod(env,cls,mid,null);//null表示无返回

④调用非静态方法

根据加载的类获取构造方法,通过构造创建对象,用对象调用非静态方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//获取构造方法
jmethodID cid;
cid=(*env)->GetMethodID(env,cls,"<init>","()V");
//创建对象用构造方法
jobj=(*env)->NewObject(env,cls,cid);
//获取方法id
mid=(*env)->GetMethodID(env,cls,"sayhello","()I");
//调用方法
r=(*env)->callIntMethod(env,jobj,mid,argv)

//设置参数,修改Java属性值
//先获取属性ID再Set
jfieldID nameID=(*env)->GetFieldID(env,cls,"name",signature);
jstr=(*env)->NewStringUTF(env,"Ethon");
(*env)->SetObjectField(env,jobj,nameID,jstr);

3.在C获得持久对象

C中的函数被调用,本身内部无法保存持久对象,可以将native的本地对象传递给Java端(可以是int或则long),当native再次需要使用时,获取java端的属性值强转为Native对象。
在C中获得全局变量
通过(*env)->NewGlobleRef(targetObj)可以获得一个native的全局引用。需要注意的是记得调用DeleteGlobalRef()释放

  • JavaVM:是指进程虚拟机环境,每个进程有且只有一个JavaVM实例
  • JNIEnv:是指线程上下文环境,每个线程有且只有一个JNIEnv实例,
您的一份奖励,就是我的一份激励