前言
在前面NDK-CMake初探中我介绍了下Android Studio搭建NDK环境的步骤,当环境搭建起来以后,我们就可以开始编写native代码与Java层进行互相交互了,下面我就来简单的介绍下如何native与Java如何进行交互。
来自native层的hello world
编写Java层代码
使用native可以修饰一个native方法,表示这个函数在c/c++中实现,System.loadLibrary();用于加载so文件。
package org.ndk.ndkfirst; public class NDKTest { static { System.loadLibrary("ndk-test-lib"); } /** * native返回一个字符串给Java层 */ public native String getStringFromNative(); }
编写相应的native代码
要想编写C/C++代码,我们首先得知道Java层的native修饰函数对应的C/C++表现形式。命名规则如下:
JNIEXPORT 返回值类型 JNICALL Java_包名_类名_函数名(JNIEnv * env, jobject obj)
上面例子中的getStringFromNative方法按照这个规则就是如下形式
JNIEXPORT jstring JNICALL Java_org_ndk_ndkfirst_NDKTest_getStringFromNative(JNIEnv * env, jobject obj);
JNIEXPORT JNICALL都是jni的关键字,表示此函数是要被jni调用的,而jstring为jni中定义的一个类型,对应Java中的String。
参数列表中的第一个参数JNIEnv* 是定义任意native函数的第一个参数(包括调用JNI的RegisterNatives函数注册的函数),指向JVM函数表的指针,函数表中的每一个入口指向一个JNI函数,每个函数用于访问JVM中特定的数据结构。JNIEnv是一个与线程相关的变量,不同线程的JNIEnv彼此独立。
第二个参数有一点特殊,对于普通函数(非static修饰),为jobject,表示当前的对象,如果是静态函数,为jclass,表示当前类。
通过上面的分析,我们可以写出如下的native代码:
#include <jni.h> extern "C" { JNIEXPORT jstring JNICALL Java_org_ndk_ndkfirst_NDKTest_getStringFromNative (JNIEnv *env, jobject obj) { //NewStringUTF表示创建一个utf-8格式的字符串 return env->NewStringUTF("hello world from native~~~"); } }
接下来按照NDK-CMake初探中的介绍配置好CMakeLists.txt文件即可。
cmake_minimum_required(VERSION 3.4.1) #设置生成的so动态库最后输出的路径 set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}) add_library(ndk-test-lib SHARED src/main/cpp/ndk_test.cpp) find_library(log-lib Specifies the name of the NDK library that log) target_link_libraries(ndk-test-lib ${log-lib} )
调用JNI函数和调用普通函数没有什么区别。如下。
NDKTest mNDKTest = new NDKTest(); Log.i(TAG, mNDKTest.getStringFromNative());
使用javah命令生成头文件
在上面介绍了JNI函数的命名规则,下面就来介绍一个使用javah命令来生成所需要的函数形式的方法。javah命令格式
javah -classpath [path1] -jni [path2]
首先介绍下path2参数,path2为含有的native方法的完整类路径,对于上面的例子,就是如下形式:
org.ndk.ndkfirst.NDKTest
path1需要指定path2完整类路径的绝对路径。比如上面的NDKTest.java文件在C:\NDKFirst\app\src\main\java\org\ndk\ndkfirst\NDKTest.java下面,那么path1就应该为C:\NDKFirst\app\src\main\java
所以最后的命令如下,最后生成的.h文件在C:\NDKFirst\app\src\main\java下,里面就有我们需要的JNI函数,copy一下就行了。
javah -classpath "C:\Users\Jay\Desktop\NDKFirst\app\src\main\java" -jni org.ndk.ndkfirst.NDKTest
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class org_ndk_ndkfirst_NDKTest */ #ifndef _Included_org_ndk_ndkfirst_NDKTest #define _Included_org_ndk_ndkfirst_NDKTest #ifdef __cplusplus extern "C" { #endif /* * Class: org_ndk_ndkfirst_NDKTest * Method: getStringFromNative * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_org_ndk_ndkfirst_NDKTest_getStringFromNative (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif
Demo地址:github