引言
在《Android NDK 使用指南(基础篇)》中,我已经介绍了NDK开发的基础知识,还不懂什么是NDK和JNI的小伙伴可以先学习一下基础篇。本文将深入探讨JNI的原理和基本工作流程,帮助读者真正掌握NDK编程。
JNI工作流程
Android中可以通过JNI实现Java和C/C++代码的相互调用,那么它们之间具体是如何交互的呢?下面将为大家进行介绍。
上图展示了Java和C/C++是如何通过JNI进行交互的,下面以基础篇中的示例代码为基础,介绍其中的几个关键步骤。
1. 加载Native库(Load JNI Library)
当Android的JVM(Java Virtual Machine)执行System.loadLibrary() 函数时, 首先会去执行C组件(.so)里的JNI_OnLoad() 函数。它主要有两个作用:
(1)设置JNI版本
如果你的.so没有提供JNI_OnLoad()函数,VM会默认该.so是使用最老的JNI 1.1版本。由于新版的JNI做了许多扩充,如果需要使用JNI的新版功能,例如JNI 1.4的java.nio.ByteBuffer,就必须借由JNI_OnLoad()函数来告知VM。
(2)设定初始值
由于VM执行到System.loadLibrary()函数时,就会立即先呼叫JNI_OnLoad(),所以C组件的开发者可以借由JNI_OnLoad()来进行C组件内的初期值之设定。
2. JNI环境处理—Java调用C++(Find Native Method)
extern "C"
表示告诉编译前使用C语言方式编译这段代码,防止对函数名被改变(因为C++允许函数重载)。
JNIEXPORT和JNICALL
JNIEXPORT用于导出函数到JVM中,JNICALL定义了函数调用的约定。
Java_com_example_myapp_NativeLib_stringFromJNI
函数名,固定以**Java_**开头,com_example_myapp对应包名,NativeLib对应类名,stringFromJNI对应方法名。
JNIEnv* env
这是一个指向JNI环境的指针,提供了一组操作Java对象和调用Java方法的函数。
jobject
这是一个引用,表示调用这个本地方法的Java对象。
3.JNI环境处理—C++调用Java(Find Java Class)
g_javaVM->AttachCurrentThread(&env, nullptr)
将当前本地线程附加到Java虚拟机,获取JNI环境。
jclass clazz = env->FindClass("com/example/myapp/NativeLib")
查找Java类com/example/myapp/NativeLib,并返回其对应的jclass对象。
jmethodID constructor = env->GetMethodID(clazz, "<init>", "()V")
获取类的构造方法ID。 “<init>” 是构造方法的特殊名称, “()V” 表示无参数且无返回值的构造方法。
jobject instance = env->NewObject(clazz, constructor)
创建NativeLib类的一个实例对象。
jmethodID methodID = env->GetMethodID(clazz, "getMessageFromJava", "()Ljava/lang/String;")
获取实例方法getMessageFromJava的ID,该方法返回一个Java字符串。
jstring message = (jstring)env->CallObjectMethod(instance, methodID)
调用getMessageFromJava方法并获取返回的Java字符串对象。
g_javaVM->DetachCurrentThread()
将当前本地线程与Java虚拟机分离。