JNI基本概念:
- JNIEnv: Java的本地化环境。C/c++访问Java的对象、类等通过JNIEnv,它里面包含了所有的API。只要和Java交互的都需要它。
- JavaVM:Java虚拟机。在一个进程里是可以创建很多个虚拟机的。但是对于Android来说,一个进程对应一个VM。每个APP对应一个JavaVM.JavaVM就是来获取JNIEnv的。它有个方法getEnv()。JNIEnv与JavaVM的关系是一个JavaVM里有很多个线程,那么每个线程对应一个JNIEnv。
- 线程。JNIEnv与线程是一一对应的。在C层去想问Java层,必须是在当前的线程下,获取到当前线程的JNIEnv,才能进行访问。否则在其他的线程里去访问另外一个线程的JNIEnv会导致Crash。
Java去调用C的过程:【以上篇文章创建的DEMO为例子】
用Java去访问C层的接口。
需要在Java层做如下事情:
- 创建native方法。[native表明了c/c++的接口]
仿写:
鼠标移到yinleiFromJNI上面,会有一个灯泡,可以快速创建C层的代码:
JNIEnv *env, jobject thiz这2个参数。
刚刚介绍了JNIEnv这是我们访问java都可以通过它去访问。
jobject 就是谁调用了这个api,就会把这个java对象当作参数传进来,传进来的时候就是通用类型的object,前面加个j就是jnienv专门为java对象设计的一个类型jobject。
在Java层没有输入任何参数,但是在生成的代码中却包含了上面的2个参数。再看模板
模板里也有这2个参数。这2个参数实际就是Java虚拟机在调用c/c++接口的时候传进来的。也就是说,Java的api通过Java调用我们前面标识的native的方法的时候,最终调用的是java虚拟机里的。Java虚拟机最终会将我们的线程环境jnienv以及调用这个方法的java对象传进来。这样在java层就可以做很多事情。有了这个java对象可以调用这个对象所提供的方法或者属性。有了这个jnienv、线程对象就知道我当前是在这个线程里做的,就直接通过env对象去调用这个相应的方法。然后通过env调用newStringUTF【Java的一个对象】,并传入了一个yinlei的字符串。它就会把c/c++的一个String转成Java对象返回回去。
- java层的native方法传入参数的时候:
无论我们传什么,它最终都会转换为jobject。
改写下它:
上述代码通过getStringUtfchars将java层的string转成了char *,拿到char *后就可以做一些操作了。
然后看到上面中的releaseStringUTFChars,是释放相应的资源。【毕竟c/c++中申请指针内存还要释放的嘛】
然后return 返回值。
然后运行查看结果:
ok,符合预想预期。
总结,编写方法时候:
- 在java层编写native的api
- 在c++层实现该api
- c++做相应的逻辑然后返回给java层,java层再做显示