1. 程式人生 > >Android Studio NDK及so檔案開發(一)

Android Studio NDK及so檔案開發(一)

部落格借鑑:

前言:

1、什麼是NDK?

NDK全稱是Native Development Kit,NDK提供了一系列的工具,幫助開發者快速開發C(或C++)的動態庫,並能自動將so和java應用一起打包成apk。NDK集成了交叉編譯器(交叉編譯器需要UNIX或LINUX系統環境),並提供了相應的mk檔案隔離CPU、平臺、ABI等差異,開發人員只需要簡單修改mk檔案(指出“哪些檔案需要編譯”、“編譯特性要求”等),就可以創建出so。

2、為什麼使用NDK?

1、程式碼的保護。由於apk的java層程式碼很容易被反編譯,而C/C++庫反匯難度較大。
2、可以方便地使用現存的開源庫。大部分現存的開源庫都是用C/C++程式碼編寫的。
3、提高程式的執行效率。將要求高效能的應用邏輯使用C開發,從而提高應用程式的執行效率。
4、便於移植。用C/C++寫得庫可以方便在其他的嵌入式平臺上再次使用。

3、什麼是JNI?

JNI的全稱是Java Native Interface,它提供了若干的API實現了Java和其他語言的通訊(主要是C和C++)。

4、為什麼使用JNI?

JNI的目的是使java方法能夠呼叫c實現的一些函式。

5、安卓中的so檔案是什麼?

android中用到的so檔案是一個c++的函式庫。在android的JNI中,要先將相應的C語言打包成so庫,然後匯入到lib資料夾中供java呼叫。

Android Studio NDK及so檔案開發

1、 NDK安裝及配置

1.1、NDK安裝

Android Studio 從1.3 Beta1開始,支援了NDK。之前則不支援,所以我們建議使用新版的編輯器。

開啟Setting => Appearance & Behavior => System Settings => Android SDK

然後在右側頁面中點選SDK Tools可以看到你是否安裝了Android NDK(如紅色框選的部分),如果未安裝那麼勾選後apply進行安裝,如果已安裝那麼接下來配置環境變數;

NDK安裝

1.2、配置環境變數

安裝好的NDk一般位於你的sdk資料夾下的ndk-bundle。

比如我這裡是:D:\SDK\ndk-bundle(如下圖),可以看到裡面有ndk-build檔案,下文進行編譯的時候我們會用到。

配置環境變數

然後將該路徑配置到你係統變數的path裡面去,如下:

配置環境變數

(我這裡使用的是Win10系統,其他系統介面不一,根據你的情況自己新增,不會請百度)

新增完畢後開啟cmd,輸入ndk-build,出現如下內容則表示成功(網上說是成功的,雖然顯示的貌似是一些錯誤資訊,但是後文執行的時候是沒問題的可以編譯成功)。

驗證環境是否配好

2、so庫開發

2.1、新建“本地”方法

如下,在MainActiviy.java中建立了一個方法

public native String getStrFromJNI();

可以看到這個方法的宣告中有native關鍵字,這個關鍵字表示這個方法是本地方法,也就是說這個方法getStrFromJNI()是通過原生代碼(C/C++)實現的,在java程式碼中僅僅是宣告。

新建本地方法

2.2、編譯該類得到對應的.h檔案

切換到Terminal,進入到該工程的 java 目錄下(如下圖所示),然後輸入

javah -jni 包名.類名(如下圖所示)。

編譯得到.h檔案

編譯成功後,重新整理下工程可以看到編譯出的.h檔案,該檔案只是為了輔助我們寫出相應的.c檔案,使用完了即可刪除。

得到的.h檔案

該檔案的程式碼如下所示:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class cn_handsomedragon_testndk_MainActivity */

#ifndef _Included_cn_handsomedragon_testndk_MainActivity
#define _Included_cn_handsomedragon_testndk_MainActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     cn_handsomedragon_testndk_MainActivity
 * Method:    getStrFromJNI
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_cn_handsomedragon_testndk_MainActivity_getStrFromJNI
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

其實重要的部分就是這一句程式碼:

JNIEXPORT jstring JNICALL Java_cn_handsomedragon_testndk_MainActivity_getStrFromJNI
  (JNIEnv *, jobject);

仔細觀察可以看到他是遵循 “Java_ 包名_ 類名_ 本地方法名” 來組織的(瞭解到這些後我們以後就可以不生成.h檔案然後直接去寫.c檔案了)。

2.3、編寫.c檔案

這時我們切換到Project,然後在app目錄下新建jni資料夾,並在裡面建立一個demo.c的c檔案(如下圖所示)。

編寫.c檔案

在demo.c檔案中編寫最基本的測試程式碼:

#include <string.h>
#include <jni.h>

jstring
Java_cn_handsomedragon_testndk_MainActivity_getStrFromJNI(JNIEnv *env,
                                                          jobject thiz) {
    return  (*env)->NewStringUTF(env, "I`m Str !");
}

這是就可以看出我們用的是.h中的那行程式碼,稍微修改為如上格式就是我們所需要的.c檔案了。

2.4、編寫Android.mk檔案

在jni目錄下新建Android.mk(必須是這個名稱Android.mk)檔案,如下圖所示:

編寫Android.mk檔案

編輯Android.mk程式碼:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE        := demo         //要生成的so庫的名稱,但實際為libdemo.so
LOCAL_SRC_FILES     := demo.c       //要使用的檔案,剛才編寫的demo.c檔案

include $(BUILD_SHARED_LIBRARY)

2.5、生成so檔案

在控制檯中,進入到工程的app目錄下,然後輸入ndk-build(如下所示),不出問題即可編譯成功。

生成so檔案

編譯完成後重新整理工程,可以看到在app目錄下生成的libs和obj資料夾,其中libs是有用的,obj資料夾無用可以刪除。libs中的可以看到生成的libdemo.so檔案。

生成的libs及obj檔案

生成的so檔案

2.6、兩個必要設定

a)、在local.properties中設定NDK路徑,我的NDK示例如下:

設定NDK路徑

b)、在app的build.gradle的android節點下設定:

節點配置

這兩處必要的地方該修改完畢後就可以開心的呼叫我們生成的so檔案了。

3、使用so檔案

  1. 將生成的libs資料夾中的內容剪下,放到app\src\main下的jniLibs資料夾內,如果沒有jniLibs資料夾就自己新建一個就好,如下圖所示:
    放入jniLibs資料夾

  2. 在MainActivity.java中,載入so檔案並呼叫,程式碼如下:

載入so庫

這個庫demo(完整的名字是libdemo.so)會在第一次使用MainActivity這個類的時候載入。(static程式碼塊宣告的程式碼會先於onCreate方法執行)

觀察控制檯的輸出,可以看到打印出來的字串:

觀察列印

此時表示so庫使用成功,之前的jni資料夾以及原來生成的.h檔案就可以完全刪除了。當然這個so庫你要做好文件的記錄,否則到時候估計你也忘了都有哪本地個方法可以呼叫了。