1.NDK 和 JNI介紹
JNI (Java Native Interface)是一套編程接口,用來實現(xiàn)Java代碼和其他語言(c羔杨、C++或匯編)進行交互壮锻。這里需要注意的是JNI是JAVA語言自己的特性伍纫,也就是說JNI和Android沒有關(guān)系铃绒。在Windows下面用JAVA做開發(fā)也經(jīng)常會用到JNI柠偶,例如:讀寫系統(tǒng)注冊表等次哈。
NDK(Native Development Kit)是Google提供的一套工具集胎署,可以讓你其他語言(C、C++或匯編)開發(fā) Android的 JNI窑滞。NDK可以編譯多平臺的so琼牧,開發(fā)人員只需要簡單修改 mk 文件說明需要的平臺,不需要改動任何代碼哀卫,NDK就可以幫你編譯出所需的so巨坊。
用JNI做應(yīng)用開發(fā)難度要比JAVA難很多,門檻也要高很多此改,如果你對C/C++把握的不好應(yīng)用還會出現(xiàn)難以發(fā)現(xiàn)的Bug趾撵!所以通常在對性能要求比較高才會使用。游戲引擎就是一個對性能要求極高的例子共啃。另外就是如果你想把核心的一些算法或處理邏輯保護起來占调,選用JNI也是一個不錯的方案。
2.方法步驟
1)下載配置ndk路徑
打開Android studio中“File”->“Project Structure”界面移剪,選擇“SDK Location”究珊,如下圖:
如果ndk位置為空,則點擊download就行挂滓,下載安裝
最后就看一下苦银,ndk路徑有沒有配置成功。如下圖:
2)添加本地方法和加載.so文件
在你要寫jni的文件中赶站,添加本地方法(我是新建一個JNIUtils類):
public static native String helloJni();
public static native int addCal(int a,int b);
加載so文件
static{
System.loadLibrary("helloJni");
}
注意:我沒有使用v7包提供的屬性和類,因為會出現(xiàn)各種問題
3)生成.h文件
首先幔虏,點擊“build”->“Make Project”,得到.class文件贝椿。如下圖:
其次想括,在studio的下邊“Terminal”中,做如下命令:
通過這一步烙博,在debug中產(chǎn)生了一個com_example_utils_JNIUtils.h的文件瑟蜈。
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_utils_JNIUtils */
#ifndef _Included_com_example_utils_JNIUtils
#define _Included_com_example_utils_JNIUtils
#ifdef __cplusplus
extern "C"{
#endif
/*
* Class: com_example_utils_JNIUtils
* Method: helloJni
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_utils_JNIUtils_helloJni (JNIEnv *, jclass);
/*
* Class: com_example_utils_JNIUtils
* Method: addCal
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_example_utils_JNIUtils_addCal (JNIEnv *, jclass, jint, jint);
#ifdef __cplusplus
}
#endif
#endif
4)創(chuàng)建jni目錄,將.h文件移動到該文件夾下
右擊Module下src中的main目錄渣窜,新建一個jni的目錄铺根,如下圖:
然后,將上一步生成的com_example_utils_JNIUtils.h文件移至該文件夾下乔宿。
5)創(chuàng)建一個.c文件在jni目錄下
在jni目錄下創(chuàng)建一個jnitest.c(文件名自己起)的文件位迂,并編寫c代碼:
#include <com_example_utils_JNIUtils.h>
/*
* Class: com_example_utils_JNIUtils
* Method: helloJni
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_utils_JNIUtils_helloJni (JNIEnv *env, jclass jobj){
return (*env)->NewStringUTF(env,"Hello JNI!");
}
/*
* Class: com_example_utils_JNIUtils
* Method: addCal
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_example_utils_JNIUtils_addCal (JNIEnv *env, jclass jobj, jint ja, jint jb){
return ja+jb;
}
要點:在頭部引入.h文件;把.h文件中的方法復(fù)制過來详瑞,并給其參數(shù)起變量名掂林。
6)在app module目錄下的build.gradle中設(shè)置庫文件名(生成的so文件名)
在Module的build.gradle的defaultConfig中添加:
ndk{
moduleName "helloJni" //生成的so名字
abiFilters "armeabi", "armeabi-v7a", "x86" //輸出指定三種abi體系結(jié)構(gòu)下的so庫。目前可有可無坝橡。
}
注意:生成的so文件名和上邊JNIUtils中靜態(tài)代碼塊中加載的so同名泻帮。
7)編譯錯誤排查
編譯過程出現(xiàn)了
Error:(13, 1) A problem occurred evaluating project ':app'.
Error: NDK integration is deprecated in the current plugin. Consider trying the new experimental plugin. For details, see [http://tools.android.com/tech-docs/new-build-system/gradle-experimental](http://tools.android.com/tech-docs/new-build-system/gradle-experimental). Set "android.useDeprecatedNdk=true" in gradle.properties to continue using the current NDK integration.
解決辦法,在工程的gradle.properties中添加:
android.useDeprecatedNdk=true
8)運行到手機上计寇,試試
3.JNI中調(diào)用java方法
在jni中回調(diào)java的方法锣杂,采用反射機制來完成。
1)找到j(luò)ava中的.class文件
jclass dpjclazz = (*env)->FindClass(env,"com/example/utils/PrintToastUtils");
2)若要調(diào)用的java方法是非靜態(tài)的方法番宁,還需獲取該類的一個對象蹲堂,具體如下:
//獲取dpjclazz的構(gòu)造函數(shù)并生成一個對象
jmethodID ctor = (*env)->GetMethodID(env,dpjclazz, "<init>", "()V"); //獲取構(gòu)造方法
jobject obj = (*env)->NewObject(env,dpjclazz, ctor);//通過該構(gòu)造方法得到一個對象
3)尋找class里面的方法
//jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);第四個參數(shù)是返回值類型
jmethodID methodID = (*env)->GetMethodID(env,dpjclazz,"printToastFromJNI","(Ljava/lang/String;)V");
具體參考:Andriod JNI編程之C++回調(diào)Java函數(shù)
4)調(diào)用方法
//void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
(*env)->CallVoidMethod(env,obj,methodID,jStr);
4.遇到錯誤
1)不能回調(diào)native方法的類中的一個非靜態(tài)方法
就主要是自己的jni出現(xiàn)錯誤,調(diào)用非靜態(tài)的方法需要先生成一個類的實例贝淤,然后才能進行調(diào)用柒竞。
2)在c代碼中寫了log的相應(yīng)代碼,LOG不能打印
解決方法:在gradle的ndk{}中添加:
ldLibs "log", "z", "m"
具體參考:
undefined reference to `__android_log_print'解決
參考資料:
- Android Studio JNI開發(fā)入門教程
- android studio 2.1.2 版本 jni 入門demo
- NDK-JNI實戰(zhàn)教程(一) 在Android Studio運行第一個NDK程序
- androidStudio ndk Cannot find 'jni.h'
- 【JNI】C中調(diào)用JAVA的參數(shù)播聪、方法
- JNI開發(fā)學(xué)習(xí)之C反射調(diào)用java方法
- "android-studio undefined reference to `__android_log_print "錯誤解決方法
- undefined reference to `__android_log_print'解決