Android NDK開發入門
網上一大堆的都是使用javah命令來產生標頭檔來完成JNI編寫,但其實ADT整合NDK後,點點滑鼠就可以了,懶人方法網上介紹很少,這邊主要講懶人JNI開發。
對於ADT配置NDK,請個人google或查看Android developer,這邊不多做介紹。
1. 建立一個Android工程,我這邊取名為JNI_Learn,一鍵產生後程式碼片段如下:
public class JNI extends ActionBarActivity {static{System.loadLibrary("JNI_Learn");}public native int plus(int x, int y); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_jni); if (savedInstanceState == null) { getSupportFragmentManager().beginTransaction() .add(R.id.container, new PlaceholderFragment()) .commit(); } Log.d("JNITest", "3+5=" + plus(3, 5)); }高亮部分是我自己添加部分,簡單解釋一下。
loadLibrary的話,裡面的名字稍後我會再標註一下,這邊主要是native層產生的so庫的名字,需要去掉lib首碼和.so尾碼。
聲明native方法要在存取權限之後,其他聲明之前,加一個native標記此方法實現在native層。
使用plus方法與正常使用無異。
2. 之後就是懶人操作了,在工程上右擊滑鼠,選擇Android Tools->Add Native Support...
之後查看工程裡面有兩個檔案
第一個是JNI_Learn.cpp,這個稍後詳述。第二個是Android.mk,在Android編譯中,都會尋找目錄下是否存在Android.mk,然後根據這個檔案進行編譯。裡面編寫了具體的編譯規則,開啟檔案後,內容如下:
LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_LDLIBS := -llogLOCAL_MODULE := JNI_LearnLOCAL_SRC_FILES := JNI_Learn.cppinclude $(BUILD_SHARED_LIBRARY)
除加紅部分外,其他都為自動產生,加紅部分是我添加為了使用native的log方法
其中LOCAL_PATH表示編譯源檔案的路徑,LOCAL_LDLIBS表示編譯模組時需要附加的連接器選項,LOCAL_MODULE表示最終編譯出來模組的名稱,LOCAL_SRC_FILES表示需要編譯的源檔案,include $(BUILD_SHARED_LIBRARY)表示最終編譯成一個共用庫檔案。
3. 下面具體添加native層對plus方法的實現代碼
#include #include #define LOG_TAG "JNITest"//log func//int __android_log_print(int prio, const char *tag, const char *fmt, ...)#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)extern "C"JNIEXPORT int Java_com_example_jni_1learn_JNI_plus");return x + y;}
使用了Android log將log列印到了logcat一樣,想瞭解的可以自行查閱相關知識,這邊沒太大關係,這個也是在Android.mk中為什麼需要增加LOCAL_LDLIBS原因。
extern “C”是必須要加的,因為C++和C編譯後的匯出函數原型不同,java只能call C類型的,所以需要將C++轉化為C類型的,增加extern "C"聲明。
JNIEXPORT聲明他是一個匯出函數。
方法名字必須以Java開頭,然後包名和類名及方法名之間底線隔開,如果不巧剛好你的包名中有底線,那如何處理JNI中java包名含底線情況呢?在前面加一個“1”。
這麼簡單程式,遇到幾個錯誤:
第一個是說JNI Load Library失敗,因為apk中可以看到so是不是被壓縮排去了(更改apk尾碼為rar,解壓rar可以看到有個lib檔案夾內有這個so),我這邊看到壓縮排去了,結果還是載入失敗。原因在於loadLibrary的參數必須要不帶首碼lib不加尾碼.so才可以。
爆出undefined reference to,原因是由於我沒有聲明extern "C",java找不到C++聲明方式的。
還有就是包名中恰好出現底線,比如JNI_Learn,需要更改為_1才可以規避。
還有就是使用LOG,必須要在Android.mk中增加LOCAL_LDLIBS。