前言
什么是NDK?
NDK全稱是Native Development Kit,NDK提供了一系列的工具,幫助開發(fā)者快速開發(fā)C(或C++)的動態(tài)庫,并能自動將so和java應用一起打包成apk。NDK集成了交叉編譯器(交叉編譯器需要UNIX或LINUX系統(tǒng)環(huán)境),并提供了相應的mk文件隔離CPU、平臺、ABI等差異,開發(fā)人員只需要簡單修改mk文件(指出“哪些文件需要編譯”、“編譯特性要求”等),就可以創(chuàng)建出so。為什么使用NDK?
1.)代碼的保護。由于apk的java層代碼很容易被反編譯,而C/C++庫反匯難度較大。
2.)可以方便地使用現(xiàn)存的開源庫。大部分現(xiàn)存的開源庫都是用C/C++代碼編寫的。
3.)提高程序的執(zhí)行效率。將要求高性能的應用邏輯使用C開發(fā),從而提高應用程序的執(zhí)行效率。
4.)便于移植。用C/C++寫得庫可以方便在其他的嵌入式平臺上再次使用。什么是JNI?
JNI全稱為:Java Native Interface。JNI 是本地編程接口,它使得在 Java 虛擬機內(nèi)部運行的 Java 代碼能夠與用其它語言(如 C、C++)編寫的代碼進行交互。為什么使用JNI?
JNI的目的是使java方法能夠調(diào)用c實現(xiàn)的一些函數(shù)。安卓中的so文件是什么?
Android中用到的so文件是一個c++的函數(shù)庫。在android的JNI中,要先將相應的C語言打包成so庫,然后導入到lib文件夾中供java調(diào)用。
本例開發(fā)環(huán)境如下:
操作系統(tǒng):Mac
開發(fā)環(huán)境:Android Studio 2.2 Beta3 + NDK r12 + Gradle 2.14.1
NDK安裝
- 從Android Studio安裝(需翻墻)
1.)打開AndroidStudio,選擇頂部工具條,Tools->Android->SDK Manager
2.)在彈出來的對話框中選擇SDK Tools選項卡
3.)勾選上圖中NDK,點擊 Apply,開始安裝
4.)安裝完成后,重啟Android Studio - 從AndroidDevTools安裝
1.)打開AndroidDevTools網(wǎng)頁,選擇導航欄中Android SDK Tools->NDK,選擇相應平臺的NDK開始下載。
2.)下載完成后,將NDK解壓到某個文件夾下,打開Android Studio,選擇File->Project Structure
在彈出來的對話框中,配置NDK路徑,如下所示:
JNI開發(fā)
下面我們就一步一步來完成一個示例,從C語言編寫的程序中獲取字符串,然后在TextView上顯示出來。
-
新建一個Android Project,命名為 MyApplication
注意:項目路徑中不能有空格!
-
項目新建完成后,默認為Android視圖,這里為了更清楚的展示,我們切換到Project視圖。
項目結(jié)構(gòu)如下:
在項目gradle.properties文件中加上以下代碼,表示我們要使用NDK進行開發(fā)。
android.useDeprecatedNdk=true
- 在項目local.properties中加入ndk和sdk的路徑:
sdk.dir=/Users/用戶名/android-sdk-macosx
ndk.dir=/Users/用戶名/android-sdk-macosx/ndk-bundle
- 在app文件夾下的build.gradle中的defaultConfig里加入如下代碼
ndk{
moduleName "hello" //生成的so文件名字,調(diào)用C程序的代碼中會用到該名字
abiFilters "armeabi", "armeabi-v7a", "x86" //輸出指定三種平臺下的so庫
}
如下所示:

- 打開布局文件activity_main.xml,我們來添加一個TextView顯示從C程序中返回的字符串
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="me.jockio.myapplication.MainActivity">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp" />
</RelativeLayout>
- 打開MainActivity.java,添加如下代碼:
public class MainActivity extends AppCompatActivity {
//固定寫法,表示我們要加載的資源文件為libhello.so
static {
System.loadLibrary("hello");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView textView = (TextView) findViewById(R.id.textView);
textView.setText(getStringFromNative());
}
//聲明一個本地方法,用native關(guān)鍵字修飾
public native String getStringFromNative();
}
- 生成.h頭文件
打開Android Studio底部的Terminal,默認命令行窗口路徑已經(jīng)在當前項目,輸入以下命令:
cd app/src/main/java
javah -jni 包名+類名

執(zhí)行完上面兩條命令后,會自動生成.h文件

生成.h文件內(nèi)容如下:

這里關(guān)鍵部分就是:
JNIEXPORT jstring JNICALL Java_me_jockio_myapplication_MainActivity_getStringFromNative (JNIEnv *, jobject);
-
新建jni文件夾,并拷貝上面生成的.h文件到jni目錄
選擇File->New->Folder->JNI Folder
在彈出的對話框中勾選Change Folder Location,并在下面輸入文件夾名,如下圖所示:
在jni目錄下,右鍵新建C文件,文件名任意,輸入如下內(nèi)容:
//引入上面生成的頭文件,并實現(xiàn)頭文件中聲明的方法
#include "me_jockio_myapplication_MainActivity.h"
JNIEXPORT jstring JNICALL Java_me_jockio_myapplication_MainActivity_getStringFromNative
(JNIEnv *env, jobject obj){
char *str="String from native C";
return (*env)->NewStringUTF(env, str);
}
注意觀察函數(shù)方法名為:Java_包名_類名_方法名,了解到這些后我們以后就可以不生成.h文件,而是直接去寫.c文件了。
-
選擇 Build->Make Project,看
app/build/intermediates/ndk/debug/lib目錄下是否生成.so文件,如果沒有生成,選擇 Build->Clean Project,等clean完成后,再Build->Rebuild Project,一般經(jīng)過上面兩步以后都能夠解決問題。
-
打開模擬器,運行Android程序。這里可以看到已經(jīng)從libhello.so文件中讀取到字符串,并顯示在了TextView中。













