寫在前面
最近這兩天非常煩躁,移植A專案的功能到B專案裡,中間牽扯到的種種資源什麼的就不說了,有耐心慢慢剝離就行。中間有一個so庫一直載入失敗,我真的是日了狗了,嘗試了各種辦法還是不行。最後用了他的sample裡的東西作為module匯入才解決,但是我覺得這並不算特別完美的解決辦法,唉,沒辦法,對ndk瞭解的還是太少,從現在開始做相應的瞭解。
第一個總是Hello World
Android Studio在現在的版本可以非常方便的生成一個帶jni的專案,只要在建立的時候勾選一個include c++ 那玩意就行了。不過對於我這種剛接觸的人來說,還是要自己折騰一下。
首先是配置ndk開發環境,其實也沒什麼要配置的,一頓下載就ok了。
這倆玩意勾選上,等他慢慢下吧。下完之後設定一下ndk的路徑:
配置好了路徑之後,我們建立一個普通的android專案,恩,不勾選c++那個選項,自己慢慢折騰。專案建立完畢之後,正確的專案目錄是這樣的:
當然你並沒有jni這個目錄,可以自己建立一個:
建立完之後在MainActivity中宣告一個native方法:
public static native String getHelloWorldStr();
複製程式碼
選中這個函式按alt+enter(mac為option + return,即android studio自動提示),點選第一個選項:
生成.c檔案原始碼如下:
#include <jni.h>
JNIEXPORT jstring JNICALL
Java_com_xiasuhuei321_studyforndk2_MainActivity_getHelloWorldStr(JNIEnv *env, jclass type) {
return (*env)->NewStringUTF(env, "Hello World!");
}
複製程式碼
其中"Hello World!"字串是我自己根據需要改成那樣的,現在不去深究c語言程式碼的意思,只管走通整個流程。用過jni的人應該知道,用jni需要根據使用者自己的class檔案生成一個.h標頭檔案,在Android中也是需要這個標頭檔案的。在android studio中怎麼生成這個標頭檔案我也是頭疼了一會,因為也沒去了解過as把class檔案輸出到哪個目錄了,好在後來找到了一個生成的方法。首先開啟android studio中自帶的Terminal,開啟就已經在專案根目錄中了,省的自己一個一個的去切換了。windows使用者可以用dir檢視目錄下有哪些檔案和資料夾,mac、Linux使用者可以用ls檢視。進入專案的/src/main/java 目錄下,輸入以下命令:
javah -d ../jni com.xiasuhuei321.studyforndk2.MainActivity
複製程式碼
這樣就在jni目錄下生成了一個.h標頭檔案
在c檔案中加入這個標頭檔案:
#include <com_xiasuhuei321_studyforndk2_MainActivity.h>
複製程式碼
接著在MainActivity中載入我們的庫:
static {
System.loadLibrary("JNI_ANDROID_TEST");
}
複製程式碼
static程式碼塊可以保證我們的程式碼在例項建立之前就被執行。在Android中生成的so一般都是有lib字首的,比如我上面用的這個名字,生成的so就會是libJNI_ANDROID_TEST.so這個名字。最後在專案app的build.gradle中的defaultConfig中加入如下程式碼:
ndk{
moduleName "JNI_ANDROID_TEST"
}
複製程式碼
這句話指定編譯生成so的名字,至於通過這種方式生成的so目錄位置,我找了一下,在這個位置:/app/build/intermediates/ndk/debug/lib/
lib中有七個abi目錄,可以根據自己的需求指定abiFilter,不用生成那麼多so。如果你對Android中的CPU架構和ABI不是非常清楚,可以自行去查閱資料。
java呼叫native方法程式碼:
tv_text = (TextView) findViewById(R.id.tv_text);
tv_text.setText(getHelloWorldStr());
複製程式碼
最後免不了看一下結果:
textview預設文字是C語言呼叫失敗,這麼一來便足以證明此次流程通了。這裡我感覺這標頭檔案是不是也可以通過gradle配置來生成,畢竟每次都這麼來一遭也挺麻煩的,後續如果我發現可以,就回來補充一下,沒有就算了。