前言
Java JNI 本意是Java Native Interface(java 本地接口)率翅,他是為了方便Java調(diào)用C练俐、C++等本地代碼所封裝的一層接口。
NDK 是android所提供的一個(gè)工具集合安聘,通過NDK可以在Android中更加方便地通過JNI來訪問本地代碼痰洒,比如C或者C++瓢棒。NDK還提供了交叉編譯器浴韭,開發(fā)人員只需要簡(jiǎn)單的修改mk文件就可以生成特定的CPU平臺(tái)的動(dòng)態(tài)庫(kù)。
使用NDK有如下好處:
- 提高代碼安全性脯宿。由于so庫(kù)反編譯比較困難念颈,因此NDK提高了Android程序的安全性。
- 可以很方便的使用目前已有的C/C++開源庫(kù)连霉。
- 便于平臺(tái)間的移植榴芳。通過C/C++實(shí)現(xiàn)的動(dòng)態(tài)庫(kù)可以很方便地在其他平臺(tái)上使用嗡靡。
- 提高程序在某些特定情形下的執(zhí)行效率,但是并不能明顯提升android程序的性能窟感。
NDK的開發(fā)流程(本文以android studio 為例)
下載配置NDK
首先到官網(wǎng)下載NDK:NDK下載入口
下載完成后讨彼,如圖找到 Project Structure 完成配置。
創(chuàng)建一個(gè)Android項(xiàng)目柿祈,并聲明所需的native方法
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.textView);
set("hello world from JniTestApp");
textView.setText(get());
}
/**
* A native method that is implemented by the 'jni-test' native library,
* which is packaged with this application.
*/
public native void set(String str);
public native String get();
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("jni-test");
}
}
實(shí)現(xiàn)Android項(xiàng)目中所聲明的native方法
-
首先說一下android studio 的自動(dòng)編譯產(chǎn)生so庫(kù)哈误。
先創(chuàng)建test.cpp文件
//test.cpp
#include <jni.h>
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
jstring Java_com_ryg_JniTestApp_MainActivity_get(JNIEnv *env, jobject thiz) {
printf("invoke get in c++\n");
return env->NewStringUTF("Hello from JNI in libjni-test.so !");
}
void Java_com_ryg_JniTestApp_MainActivity_set(JNIEnv *env, jobject thiz, jstring string) {
printf("invoke set from C++\n");
char* str = (char*)env->GetStringUTFChars(string,NULL);
printf("%s\n", str);
env->ReleaseStringUTFChars(string, str);
}
#ifdef __cplusplus
}
#endif
為了能夠讓androidStudio自動(dòng)編譯JNI代碼,首先需要在APP 的build.grade 的 defaultConfig 區(qū)域內(nèi)添加NDK選項(xiàng)躏嚎,其中moduleName指定了模塊的名稱蜜自,這個(gè)名稱指定了打包后的so庫(kù)的文件名。
android {
...
defaultConfig {
...
ndk {
moduleName "jni-test"
}
}
}
接著需要將JNI的代碼放在 app/src/main/jni 目錄下卢佣,
注意存放 JNI代碼的目錄名必須為jni重荠,如果不想采用jni這個(gè)名稱,可以通過如下方式來指定JNI的代碼路徑虚茶,其中jni.srcDirs 指定了JNI 代碼的路徑:
android {
...
sourceSets.main {
jni.srcDirs 'src/main/jni_src'
}
}
經(jīng)過上面步驟戈鲁,AndroidStudio就可以自動(dòng)編譯JNI代碼了,但是這個(gè)時(shí)候AndroidStudio 會(huì)把所有CPU平臺(tái)的so庫(kù)都打包apk嘹叫,一般來說實(shí)際開發(fā)只需要打包armeabi 平臺(tái)的so庫(kù)即可和蚪。 解決這個(gè)問題也簡(jiǎn)單,按照如下方式修改build.gradle配置阀参。
android {
...
productFlavors {
arm {
ndk {
abiFilter "armeabi"
}
}
x86 {
ndk {
abiFilter "x86"
}
}
}
}
然后找到Build Variants 面板中選擇armDebug 選項(xiàng)進(jìn)行編輯就可以了摔踱。
- 除了以上方法,我們還可以使用NDK 手動(dòng)的去生成所需要的so庫(kù)
這里首先我們需要配置我們的環(huán)境變量(Macbook為例):
首先打開配置文件
open ~/.bash_profile
然后添加如下信息:
export PATH=/Users/vince/Downloads/adt-bundle-mac-x86_64-20140702/sdk/ndk-bundle/:$PATH
其中 /Users/vince/Downloads/adt-bundle-mac-x86_64-20140702/sdk/ndk-bundle/是我本地NDK的存放路徑暮蹂。
添加完成后寞缝,執(zhí)行
source ~/.bash_profile
來刷新剛設(shè)置好的環(huán)境變量。設(shè)置完成后仰泻,我們通過 ndk-build 命令來編譯生成so庫(kù)荆陆。
接下來需要 在外部創(chuàng)建一個(gè)名為jni的目錄,然后創(chuàng)建三個(gè)文件:test.cpp集侯、Android.mk被啼、Application.mk。
//Application.mk
APP_ABI := armeabi
在Application.mk中棠枉,常用的配置項(xiàng)是 APP_ABI 浓体,表示CPU的架構(gòu)平臺(tái)的類型,常見的架構(gòu)平臺(tái)有 armeabi辈讶、x86命浴、mips,其中移動(dòng)設(shè)備中占據(jù)主要地位的是armeabi,這也是大部分apk中只包含armeabi類型的so庫(kù)的原因生闲。默認(rèn)情況下NDK會(huì)編譯產(chǎn)生各個(gè)CPU平臺(tái)的so庫(kù)媳溺,通過AOO_ABI選項(xiàng)即可指定so庫(kù)的CPU平臺(tái)的類型,比如armeabi碍讯,這樣NDK只會(huì)編譯armeabi平臺(tái)下的so庫(kù)悬蔽。
//Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := jni-test
LOCAL_SRC_FILES := test.cpp
include $(BUILD_SHARED_LIBRARY)
在Android.mk中,LOCAL_MODULE 表示模塊的名稱捉兴,LOCAL_SRC_FILES 表示需要參與編譯的源文件屯阀。
接下來,終端界面(命令行界面)切換到j(luò)ni目錄的父目錄轴术,然后運(yùn)行ndk-build 命令編譯生成so庫(kù)难衰。
編譯過程中,NDK會(huì)創(chuàng)建一個(gè)和jni目錄平級(jí)的目錄libs逗栽,libs下面就存放著生成的so庫(kù)盖袭。需要注意的是,ndk-build 命令默認(rèn)指定jni目錄為bending源碼的目錄彼宠,如果源碼存放的目錄名不是jni鳄虱,那么 ndk-build 則無法成功編譯。
so庫(kù)的引入使用
在 app/src/main 中創(chuàng)建一個(gè)名為jniLibs 的目錄凭峡,將生成的so庫(kù)復(fù)制到j(luò)niLibs 目錄中拙已,然后通過AndroidStudio 編譯運(yùn)行即可。
上面步驟需要將NDK編譯的so庫(kù)放置在 jniLibs目錄下摧冀,這個(gè)是AndroidStudio 所識(shí)別的默認(rèn)目錄倍踪,如果想使用其他目錄,可以修改App的build.gradle 文件索昂。
android{
...
sourceSets.main{
jniLibs.srcDir 'src/main/jni_libs'
}
}
最后
附上demo的地址 : https://github.com/woshishenxian/NdkDemo