1. 安裝NDK和ADT
其中NDK用於編譯C++代碼,而ADT用於編譯Java代碼及產生apk包。
NDK雖然可以也可以在Windows下通過Cywin來運行,但是會遇到命令列參數過長的問題(檔案比較多的時候)。因此,建議把NDK安裝在Linux系統上。
ADT則運行在Windows下,並不需要NDK,而只要把Linux下編譯好的.so檔案拷貝過來即可。
2. 編譯libevent
擷取libevent源碼,在linux系統下進行交叉編譯。 關鍵是./configure時,通過參數傳入NDK提供的交叉編譯工具,include和lib的路徑
會遇到一些小問題,細節請參照這篇文章Cross Compiling libevent for Android
就可以搞定。
最後make,就可以在.libs下看到編譯好的.so和.a檔案。
更多NDK交叉編譯的問題,可以看這篇文章Building Open Source libraries with Android NDK。
3. 編譯工程代碼
在NDK的samples目錄下建立工程目錄,比如myprj,然後再建立一個子目錄jni,把C++代碼拷入其中。
在jni目錄下建立Android.mk檔案,其內容如下
LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)#引入已經編譯好的檔案libevent.aLOCAL_MODULE := eventLOCAL_SRC_FILES := libevent/src/.libs/libevent.ainclude $(PREBUILT_STATIC_LIBRARY)include $(CLEAR_VARS)#引入已經編譯好的檔案libevent_pthreads.aLOCAL_MODULE := event_pthreadsLOCAL_SRC_FILES := libevent/src/.libs/libevent_pthreads.ainclude $(PREBUILT_STATIC_LIBRARY)include $(CLEAR_VARS)#編譯CPP源碼LOCAL_MODULE := libxyzLOCAL_C_INCLUDES := libevent/src/includeLOCAL_C_INCLUDES += libxyz/#引用多級目錄下的所有的CPP檔案MY_FILES := $(wildcard libxyz/*.cpp)MY_FILES += $(wildcard libxyz/*/*.cpp)LOCAL_SRC_FILES += $(MY_FILES)#引用zlib庫,以及android的log庫LOCAL_LDLIBS += -lz -llog#引用前面定義的libevent靜態庫LOCAL_STATIC_LIBRARIES := eventLOCAL_STATIC_LIBRARIES += event_pthreads#編譯產生動態庫include $(BUILD_SHARED_LIBRARY)
需要說明的是,我們這裡使用的是libevent靜態庫,而不是動態庫。這是由於Android平台不支援versioned soname,即形如libevent.so.5.0這樣的檔案名稱。即便按照這篇文章介紹的,修改soname之後,libevent_pthreads.so載入時還是會遇到reloc失敗的問題。為何如此,通過objdump
-x查看,好像是libevent_pthreads.so匯入表裡沒有對libevent.so的引用,結果有些符號死活就是找不到。規避辦法,就是引用靜態庫。
日誌輸出,不使用printf,而可以使用__android_log_print()函數,這樣就可以在ADT的LogCat控制台看到輸出的日誌。
C++與Java之間綁定,使用JNI。參照hello_jni的例子,建立libxyz_jni.cpp檔案,為每個匯出函數產生綁定函數。比如
extern "C"{void Java_com_company_myprj_libxyz_foo(JNIEnv* env, jobject thiz){ libxyz_foo();}}
需要注意的是,最後的函數名稱foo不要帶"_",否則跟java端綁定就不能成功。
在jni目錄下運行ndk-build,就可以在上級的libs/armeabi下看到產生的libxyz.so檔案。
通過objdump -T libxyz.so就可以看到所有的匯出函數。
4.Java工程
建立普通的android工程,並把前面編譯得到libxyz.so放入libs/armeabi目錄下。
建立libxyz.java檔案,用於對libxyz.so引用,如下。
package com.company.myprj;public class libxyz {public native void foo();static{System.loadLibrary("xyz");}}
注意看清楚,package,class和method的名稱與.so的匯出函數名稱是如何一一對應的。
最後,在android工程裡合適的位置,就可以調用以上代碼了。
import com.company.myprj.libxyz;new libxyz().foo();
另外,需要在manifest檔案裡,給應用加上INTERNET存取權限。
<uses-permission android:name="android.permission.INTERNET"/>
如果是在模擬器裡運行,需要通過
adb forward tcp:1234 tcp:8080
把模擬器裡的8080連接埠轉成外部的1234連接埠,這樣就可以在PC機上訪問了。