JNI調(diào)用

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)方法

Paste_Image.png

就主要是自己的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'解決

參考資料:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末朽基,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子离陶,更是在濱河造成了極大的恐慌稼虎,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件招刨,死亡現(xiàn)場離奇詭異霎俩,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進店門打却,熙熙樓的掌柜王于貴愁眉苦臉地迎上來杉适,“玉大人,你說我怎么就攤上這事柳击≡惩疲” “怎么了?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵捌肴,是天一觀的道長蹬叭。 經(jīng)常有香客問我,道長状知,這世上最難降的妖魔是什么秽五? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮饥悴,結(jié)果婚禮上筝蚕,老公的妹妹穿的比我還像新娘。我一直安慰自己铺坞,他們只是感情好起宽,可當(dāng)我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著济榨,像睡著了一般坯沪。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上擒滑,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天腐晾,我揣著相機與錄音,去河邊找鬼丐一。 笑死藻糖,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的库车。 我是一名探鬼主播巨柒,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼柠衍!你這毒婦竟也來了洋满?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤珍坊,失蹤者是張志新(化名)和其女友劉穎牺勾,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體阵漏,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡驻民,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年翻具,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片回还。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡裆泳,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出懦趋,到底是詐尸還是另有隱情,我是刑警寧澤疹味,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布仅叫,位于F島的核電站,受9級特大地震影響糙捺,放射性物質(zhì)發(fā)生泄漏诫咱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一洪灯、第九天 我趴在偏房一處隱蔽的房頂上張望坎缭。 院中可真熱鬧,春花似錦签钩、人聲如沸掏呼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽憎夷。三九已至,卻和暖如春昧旨,著一層夾襖步出監(jiān)牢的瞬間拾给,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工兔沃, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蒋得,地道東北人。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓乒疏,卻偏偏與公主長得像额衙,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子怕吴,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,465評論 2 348

推薦閱讀更多精彩內(nèi)容