JNI不必多说,是Java与Native的桥梁,也许不经常使用,但必须会使用。
流程也是一致的:
- 创建带有native方法的Java类
- 使用javah编译上面的类,得到.h文件
- 拷贝头文件部分内容,写成.c文件
- 制定makefile
- 使用ndk编译.c得到.so文件
- 使用System.loadLibary等方法加载so文件调用
接下来我们就…
稍等,为了不去终端敲那些javah/ndk之类的命令,先去建个快捷脚本吧。当然不是使用py,而是在Idea的tools中添加。
准备工作
Idea系列的IDE应该都有这个选项,然后添加,这里主要添加javah和ndk build就行了。
先生成javah相关的工具:
所有环境变量和项目变量都可以在右侧选择。
javah的三项应该可以填:
$JDKPath$\bin\javah.exe
-classpath . -jni -d $ModuleFileDir$\src\main\jni $FileClass$
$ModuleFileDir$\src\main\java
看一下javah的大概参数,就可以明白中间参数的意义,整个工具的意义就是编译当前Java文件的class文件,在\src\main\jni目录生成.h头文件。
再生成ndk相关的工具:
ndk这边比较简单,直接找到ndk-build.cmd所在位置就行了。
D:\ANDROID\android-sdk-windows\ndk-bundle\ndk-build.cmd
$ModuleFileDir$\src\main\java
另外,还有makefile,不过这个文件有固定模板,目前不需要深究。这个文件命名为—Android.mk,最好和c文件或cpp文件在同一目录。
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE :=device-b-jni
LOCAL_SRC_FILES :=device_b.c
include $(BUILD_SHARED_LIBRARY)
理论上,只需要更改LOCAL_MODULE 与LOCAL_SRC_FILES 两个值就行了。
生成so
准备工作完成,现在可以按流程来了。
第一步,创建带native的Java类;
public class Chain {
public static native int getLength(String content);
}
第二步,使用javah编译,这里可以使用之前准备的javah Tools,控制台会打印相关信息;
"C:\Program Files\Java\jdk1.8.0_60\bin\javah.exe" -classpath . -jni -d E:\Projects\SmartBccaStreamGateway\app\src\main\jni com.jinxin.smartbccastreamgateway.Chain
这一步完成后就会在jni目录生成一个.h文件:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_jinxin_smartbccastreamgateway_Chain */
#ifndef _Included_com_jinxin_smartbccastreamgateway_Chain
#define _Included_com_jinxin_smartbccastreamgateway_Chain
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_jinxin_smartbccastreamgateway_Chain
* Method: getLength
* Signature: (Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_com_jinxin_smartbccastreamgateway_Chain_getLength
(JNIEnv *, jclass, jstring);
#ifdef __cplusplus
}
#endif
#endif
第三步,复制上面的.h文件的部分内容,写成.c文件;
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <com_jinxin_smartbccastreamgateway_Chain.h>
/*
* Class: com_jinxin_smartbccastreamgateway_Chain
* Method: getLength
* Signature: (Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_com_jinxin_smartbccastreamgateway_Chain_getLength
(JNIEnv *env, jobject obj, jstring str){
return (*env)->GetStringUTFLength(env,str);
}
可以看到,主要步骤是替换头文件标示,删除除函数之外的代码,以及实现函数内容。
第四步,制定makefile,有模板,修改要编译的c文件路径即可,命名为----Android.mk;
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE :=chain-jni
LOCAL_SRC_FILES :=chain.c
include $(BUILD_SHARED_LIBRARY)
第五步,使用ndk编译,会在控制台显示相关信息;
D:\ANDROID\android-sdk-windows\ndk-bundle\ndk-build.cmd
Android NDK: APP_PLATFORM not set. Defaulting to minimum supported version android-16.
Android NDK: WARNING: APP_PLATFORM android-16 is higher than android:minSdkVersion 1 in E:/Projects/SmartBccaStreamGateway/app/src/main/AndroidManifest.xml. NDK binaries will *not* be compatible with devices older than android-16. See https://android.googlesource.com/platform/ndk/+/master/docs/user/common_problems.md for more information.
[arm64-v8a] Compile : chain-jni <= chain.c
[arm64-v8a] SharedLibrary : libchain-jni.so
[arm64-v8a] Install : libchain-jni.so => libs/arm64-v8a/libchain-jni.so
[armeabi-v7a] Compile thumb : chain-jni <= chain.c
[armeabi-v7a] SharedLibrary : libchain-jni.so
[armeabi-v7a] Install : libchain-jni.so => libs/armeabi-v7a/libchain-jni.so
[x86] Compile : chain-jni <= chain.c
[x86] SharedLibrary : libchain-jni.so
[x86] Install : libchain-jni.so => libs/x86/libchain-jni.so
[x86_64] Compile : chain-jni <= chain.c
[x86_64] SharedLibrary : libchain-jni.so
[x86_64] Install : libchain-jni.so => libs/x86_64/libchain-jni.so
最终会生成so文件:
第六步,加载so文件,调试方法;把上面生成的so文件放到项目的jniLibs目录下,然后加载so文件;
public class Chain {
static{
System.loadLibrary("chain-jni");
}
public static native int getLength(String content);
}
我这里就在这个类里面加载了,然后在其他地方调用:
System.out.println(Chain.getLength("I'm fine."));
最终输出:
07-12 10:56:28.355 I/System.out: 9
成功。