其實(shí)NDK開(kāi)發(fā)具體操作在NDK官網(wǎng)Guides中已經(jīng)有了很詳細(xì)的介紹。。這里主要記錄一下我自己的操作過(guò)程和遇到的坑
1、準(zhǔn)備工作
先配置好AndroidStudio的NDK環(huán)境
- 從NDK官網(wǎng)下載合適的NDK版本
- 下載好NDK開(kāi)發(fā)包之后解壓到自己指定的目錄中
- 設(shè)置AndroidStudio 的NDK路徑,有兩種設(shè)置方法
- 可以通過(guò)local.properties文件設(shè)置


- 也可以通過(guò)AndroidStudio來(lái)設(shè)置


2、項(xiàng)目配置
添加C/C++ 代碼有兩種情況,一種是有.so文件,第二種是有源碼需要編譯出.so
2.1 已有.so 文件的情況
2.1.1 把.so文件添加到工程路徑app/libs 下對(duì)應(yīng)的CPU架構(gòu)的文件夾下

2.1.2 然后在build.gradle中配置jni庫(kù)的尋找路徑
sourceSets.main { jniLibs.srcDirs = ['libs']}

當(dāng)然這里就可以指定其他路徑,然后把.so文件放到對(duì)應(yīng)路徑下的CPU架構(gòu)的文件夾下
注意 之前看一篇文章說(shuō)libs是AndroidStudio默認(rèn)的搜索路徑,所以我就沒(méi)到build.gradle中添加配置,導(dǎo)致jni調(diào)用的函數(shù)一直沒(méi)有找到而拋出異常。不知道是不是我自己的操作問(wèn)題,不過(guò)加上配置就都正常了
2.2 有源碼,需要編譯出.so
編譯.so有兩種方式,第一種通過(guò)手動(dòng)執(zhí)行命令編譯出.so文件,具體可以看另一篇文章ndk-build生成.so
第二種是通過(guò)添加相應(yīng)配置,讓AndroidStudio來(lái)編譯源碼
#######2.2.1 創(chuàng)建放源碼的jni文件夾
源碼存放的默認(rèn)位置為app/src/main/jni中

如果工程中沒(méi)有這個(gè)文件夾,可以通過(guò)菜單創(chuàng)建,當(dāng)然也可以直接在這個(gè)路徑下創(chuàng)建一個(gè)叫jni的文件夾

還可以自定義目錄,然后在build.gradle中做一個(gè)資源路徑指定即可(不過(guò)這種方式我沒(méi)驗(yàn)證過(guò),后面試了一下好像是錯(cuò)的):

#######2.2.2 添加編譯相關(guān)的配置
將源碼放到2.2.1中創(chuàng)建的jni目錄下,并在該目錄下添加編譯需要的配置文件,主要就是Android.mk和Application.mk,這兩個(gè)配置文件和ndk-build生成.so中提到的是相同的,相關(guān)配置西甲誒可以看NDK官網(wǎng)build教程
也可以通過(guò)在 build.gradle中添加NDK的編譯配置,這樣就不用添加Android.mk和Application.mk了

ndk{
moduleName "jniLib" // 模塊名,編譯出來(lái)的 .so 文件
// 指定編譯平臺(tái)
// 更多平臺(tái)信息 參見(jiàn)https://developer.android.com/ndk/guides/abis.html#sa
abiFilters "armeabi", "armeabi-v7a", "x86"
\* Other ndk flags configurable here are
* cppFlags.add("-fno-rtti")
* cppFlags.add("-fno-exceptions")
* ldLibs.addAll(["android", "log"])
* stl = "system"
*/
}
使用gradle的好處是,自動(dòng)編譯生成apk文件,并且把相關(guān)的.so文件打包到apk安裝包中
3、Java代碼中調(diào)用
package com.zzl.injecthost;
public class NdkJniUtils {
static{
System.loadLibrary("jniLib");
}
public native String StringFromJNI();
}
這里System.loadLibrary("jniLib"); 這個(gè)jniLib即為在2.2.2中build.gradle內(nèi)配置的moduleName

NdkJniUtils jni = new NdkJniUtils();
textView.setText(jni.StringFromJNI());

這里有個(gè)注意點(diǎn)
NDK下的C/C++函數(shù)和Java橋接的函數(shù)命名是有約束的,規(guī)則如下:
Java_PackageName_ClassName_MethodName
例如下面,我有個(gè)NdkJniUtils類在com.zzl.injecthost中,在這個(gè)類中聲明一個(gè)native的jni函數(shù),則C/C++對(duì)應(yīng)的函數(shù)的簽名應(yīng)該為:
Java_com_zzl_injethost_NdkJniUtils_StringFromJNI(JNIEnv *env, jobject obj)
{
return (*env)->NewStringUTF(env, "Hello from JNI !");
}