開始之前
最近學習了一下NDK的開發(fā), 就來分享一下.
對一個新鮮事物, 我們先解決的無非就是三件事情: 是什么?為什么?怎么做?.
NDK簡介
(英語:native development kit嘹履,簡稱NDK)是一種基于原生程序接口的軟件開發(fā)工具锋恬。通過此工具開發(fā)的程序直接以本地語言運行砂碉,而非虛擬機膀懈。因此只有java等基于虛擬機運行的語言的程序才會有原生開發(fā)工具包荣暮。[維基百科]
- NDK是一系列工具的集合
NDK提供了一系列的工具颈走,幫助開發(fā)者快速開發(fā)C(或C++)的動態(tài)庫民轴,并能自動將so和java應用一起打包成apk。這些工具對開發(fā)者的幫助是巨大的.
- NDK集成了交叉編譯器踊谋,并提供了相應的mk文件隔離CPU蝉仇、平臺、ABI等差異,開發(fā)人員只需要簡單修改mk文件(指出“哪些文件需要編譯”轿衔、“編譯特性要求”等)沉迹,就可以創(chuàng)建出so。
NDK可以自動地將so和Java應用一起打包害驹,極大地減輕了開發(fā)人員的打包工作胚股。
那我們?yōu)槭裁匆褂媚?
- 代碼的保護。由于apk的java層代碼很容易被反編譯裙秋,而C/C++庫反匯難度較大。
- 可以方便地使用現(xiàn)存的開源庫缨伊。大部分現(xiàn)存的開源庫都是用C/C++代碼編寫的摘刑。
- 提高程序的執(zhí)行效率。將要求高性能的應用邏輯使用C開發(fā)刻坊,從而提高應用程序的執(zhí)行效率枷恕。
- 便于移植。用C/C++寫得庫可以方便在其他的嵌入式平臺上再次使用谭胚。
上述文字致謝Devin Zhang提供理論支持
是什么和為什么我就先介紹到這兒, 接下來就具體看看如何進行NDK的開發(fā)
開發(fā)前準備
開發(fā)環(huán)境
請大家務必升級到AS 2.2以上版本, 因為這個版本升級了很多內容, 詳情請見 Android Studio 2.2 正式穩(wěn)定版發(fā)布
- Android Studio 2.2.2
- JDK1.7
- API 24
- Gradle 2.2.2
NDK和CMake 的下載和安裝
大家可以直接打開SDK進行下載和安裝
由于NDK的工具包較大, 大家也可以選擇從網(wǎng)站中下載: http://wear.techbrood.com/tools/sdk/ndk/, 選擇自己對應的版本使用迅雷等工具下載即可, 不過通過這種方法一定要修改local.properties文件, 在里面添加:
//后面改成自己下載后解壓的路徑名
ndk.dir=C\:\\Users\\Lulu\\AppData\\Local\\Android\\android-ndk-r13
關于CMake
- CMakeList.txt 是腳本文件, 需要指定包含哪些源代碼;
- 可以寫一些條件語句, 實現(xiàn)不同的代碼包含
- 內部說明:
add_library 表示編譯一個代碼庫, 內部包含了代碼庫的名稱, 以及源代碼有哪些
NDK兩種開發(fā)模式
- ndk-build 形式; Android Studio 2.2之前的模式
- CMake 形式: CLion C/C++編輯器; AS2.2之后整合了CLion代碼, AS就支持了CMake形式的NDK開發(fā)
開始開發(fā)
接下來通過幾個案例來演示NDK的開發(fā)流程
創(chuàng)建工程
-
新建工程, 選中Include C++ Support
Include C++ Support -
一路Next之后, 在最后Finish頁面盡量選中圖示兩項, 這樣會給我們包裹一些特定的示例代碼, 幫助我們理解和使用
NDK -
點擊Finish, 如果出現(xiàn)圖示錯誤的肯定沒有好好看上面的 開發(fā)前準備
ERROR
案例一 實現(xiàn)在C語言中隱藏AppKey等信息
step1: 為了使代碼整潔, 咱們新建一個類 NativeHelper, 專門用于訪問C語言代碼的幫助類 并添加獲取Appkey的方法
public class NativeHelper {
static {
// 加載C代碼庫, 庫的名稱, 必須是CMakeLists.txt中指定的名稱
System.loadLibrary("native-lib");
}
//獲取C中隱藏的AppKey
public static native String getAppKey();
}
Note: 一定要添加上面的靜態(tài)代碼塊的內容, 否則無法加載C代碼庫
此時的getAppKey()方法標紅, 不用管它, 繼續(xù)....
step2: 在cpp目錄下右擊創(chuàng)建C/C++ Source, 選擇Type, 并勾選 Create an associated hader, 為保持對應, 名字命名為: com_lulu_ndkdemo_NativeHelper, 此時會出現(xiàn), 在cpp目錄下會出現(xiàn)兩個文件, 如圖:
step3: 在CMakeLists.txt中的add_library中添加依關系, 點擊同步
src/main/cpp/com_lulu_ndkdemo_NativeHelper.c
Note:
C代碼庫生成的名稱規(guī)則
- 如果棧頂代碼庫名稱為 "nh" 那么生成的文件必定是libnh.so
命名規(guī)則: lib庫名.so - System.loadLibrary(庫名); //此處不能包含前面的lib和后面的.so
step4: 在com_lulu_ndkdemo_NativeHelper.c文件中添加c語言代碼
#include <jni.h>
JNIEXPORT jstring JNICALL
Java_com_lulu_ndkdemo_NativeHelper_getAppKey(JNIEnv *env, jclass type) {
//測試代碼, 沒有任何意義
char* app_key = "5465465416948";
//生成 Java 中的字符串對象
//指針的指針
// env <=> JNINativeInterface** C語言
return (*env)->NewStringUTF(env, app_key);
}
step5: 在MainActivity中獲取AppKey, 查看結果 -> 成功
String appKey = NativeHelper.getAppKey();
Log.d(TAG, "onCreate: appKey => " + appKey);
Note: Java 調用C/C++代碼
- 任何一個類的方法, 如果聲明了native修飾符, 那么就可以認為是一個C代碼;
- 可以用對象, 類直接調用
- 創(chuàng)建C/C++文件; 如果一個類中有一個native的方法, 那么對應的C方法: Java_包名類名方法名(JNIEnv *env, ...);
- 當Java類中包含了native的方法, 那么這個類必須寫一個靜態(tài)初初始化塊: System.loadLibrary("庫名")
案例二 實現(xiàn)在C語言中打印log
接下來, 只簡單介紹核心代碼, 不再贅述
step1: 在com_lulu_ndkdemo_NativeHelper.c中添加:
JNIEXPORT void JNICALL
Java_com_lulu_ndkdemo_NativeHelper_printLog(JNIEnv *env, jclass type, jstring str_) {
const char *str = (*env)->GetStringUTFChars(env, str_, 0);
//TODO: 顯示Android 的日志
// 調用Android的代碼
// 代碼需要調用系統(tǒng)的日志庫, 這個庫已經(jīng)在 CMakeList.txt添加了e,因此可以直接調用
const char *tag = "NativeHelper";
//jstring -> char*
jboolean b = JNI_FALSE;
const char* txt = (*env)->GetStringUTFChars(env, str_, b);
//打印log日志
__android_log_write(ANDROID_LOG_DEBUG, tag, txt);
//釋放string
(*env)->ReleaseStringUTFChars(env, str_, str);
}
step2: NativeHelper中添加
//在C中打印log
public static native void printLog(String str);
step3: MainActivity中調用
//打印C語言中的Log
NativeHelper.printLog("測試Log");
完整代碼
Demo已上傳到github上, 歡迎大家Clone
小結
至此, 咱們應該大致了解了一下NDK開發(fā)的簡單流程, 鄙人菜鳥, 希望拋磚引玉, "引"出更好的文章
本文還有下篇, 將再寫一些關于NDK開發(fā)的案例Demo, 希望大家喜歡和關注