標籤:android jni 打包多個so ndk 移植
之前在進行cocos2dx開發時,已經詳細介紹了如何將win32的c++代碼移植到Android平台,當再次回顧時,發現一些基礎的東西理解並不是很徹底,今天使用Android NDK提供的一個例子做一個簡單的移植。在進行該demo前,請確認你已經配置了Android開發環境和安裝了最新的Android NDK。
1.建立Android項目
建立一個Android項目 , 包名是com.example.hellojni,建立一個Activity作為程式進入的Acitivity,命名為HelloJni。
2.建立 C 檔案
建立一個C檔案,放一個函數,該函數的作用是擷取當前cpu架構並以字串的形式返回。請注意該函數的格式: Java_包名的底線串連_Java檔案名稱_java函數名。
#include <string.h>#include <jni.h>jstringJava_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env, jobject thiz ){#if defined(__arm__) #if defined(__ARM_ARCH_7A__) #if defined(__ARM_NEON__) #define ABI "armeabi-v7a/NEON" #else #define ABI "armeabi-v7a" #endif #else #define ABI "armeabi" #endif#elif defined(__i386__) #define ABI "x86"#elif defined(__mips__) #define ABI "mips"#else #define ABI "unknown"#endif return (*env)->NewStringUTF(env, "Hello from JNI ! Compiled with ABI " ABI ".");}
3.配置JNI
在該Android項目的根目錄(即AndroidManifest.xml檔案所在目錄)下建立一個檔案夾,命名為jni(注意,檔案名稱不能寫錯哦)
在jni目錄下,建立Android.mk和Application.mk兩個檔案,同時將C檔案也放進jni檔案夾下面來。如下:
這裡的nick檔案夾,可以先忽略不看,這是為後面的打包多個so準備的。
a.配置Android.mk檔案
Android.mk檔案實際上一個很小的NDK構建指令碼,它的文法在: NDK安裝目錄/docs/ANDROID-MK.html,下文代碼也對一些基本屬性添加了注釋。
#返回當前檔案在系統中的路徑,mk檔案開始時必須定義該變數LOCAL_PATH := $(call my-dir)#CLEAR_VARS 變數由構建系統提供,因為有大量的全域變數,在本次構建前,清除上一次的include $(CLEAR_VARS)#LOCAL_MODULE 實際是項目名,用於區分各個項目,名字必須是唯一的而且不包含空格,最終的so庫,命名也會是 lib項目名.soLOCAL_MODULE := hello-jni#要編譯的c or cpp檔案,注意不需要在這裡列舉標頭檔或者include的檔案,構建系統會自動幫你依賴這些檔案LOCAL_SRC_FILES := hello-jni.c#構建系統提供的變數include $(BUILD_SHARED_LIBRARY)
b.配置Application.mk檔案
Application.mk檔案實際上是對應用程式本身描述的檔案,它定義了應用程式需要的功能模組的列表、針對不同cpu架構打包不同的so]、要構建release或者debug包等。
APP_ABI := XXX,這裡的XXX就是指不同的平台,可以選填的有x86,armeabi,armeabi-v7a,mips,all,值得一提的是,選擇all,則會構建出所有平台的so,如果不填該項,預設構建為armeabi的。同時,作者也做過一個實驗,構建armeabi平台的so是可以運行在intel x86架構cpu平台的,但是構建x86平台的so則不能在armeabi平台上啟動並執行,這樣看來,應該是intel針對armeabi做了相容,但是如果想要so 以最小的能耗運行在intel x86平台,還是要指定構建的so為x86平台。
4.打包so以及如何打包多個so
在當前Android項目的根目錄下,運行 NDK安裝路徑/ndk-build,則開始打包so。
另外,如果運行 NDK安裝路徑/ndk-build clean,會clean當前所有的so;
運行 NDK安裝路徑/ndk-build -B V=1,則強制重新打包,
如果想要打包多個so,則可以在Android.mk定義多個modules,或者寫多個Android.mk,每個Android.mk定義一個modules,我這裡在jni目錄下又建立了一個nick檔案夾,用於放置新的C檔案。
此時,只需要改動jni目錄下的Android.mk,再次對nick檔案夾的C代碼打包即可。jni下的Android.mk檔案:
#返回當前檔案在系統中的路徑,mk檔案開始時必須定義該變數LOCAL_PATH := $(call my-dir)#CLEAR_VARS 變數由構建系統提供,因為有大量的全域變數,在本次構建前,清除上一次的include $(CLEAR_VARS)#LOCAL_MODULE 實際是項目名,用於區分各個項目,名字必須是唯一的而且不包含空格,最終的so庫,命名也會是 lib項目名.soLOCAL_MODULE := hello-jni#要編譯的c or cpp檔案,注意不需要在這裡列舉標頭檔或者include的檔案,構建系統會自動幫你依賴這些檔案LOCAL_SRC_FILES := hello-jni.c#構建系統提供的變數include $(BUILD_SHARED_LIBRARY)#對nick檔案夾下的代碼打包soinclude $(CLEAR_VARS)LOCAL_MODULE := hello-jni-mineLOCAL_SRC_FILES := nick/hello-jni.cinclude $(BUILD_SHARED_LIBRARY)
是的,你沒看錯,重新加上LOCAL_MODULE和LOCAL_SRC_FILES變數重新設定一下即可。
5.jni調用
在Activity中,我們使用static 關鍵詞將載入so放在函數體中,以保證直接先載入so.
static { System.loadLibrary("hello-jni"); }要注意的是,System.loadLibrary()中填寫的並不是完整的so名,而是去掉首碼lib和尾碼.so的,也就是Android.mk中的LOCAL_MODULE變數。
java層的函數要用native關鍵詞聲明這次調用native層的函數,如果該java函數是public native String XXXX(),那麼在這裡就是調用C代碼中的Java_com_example_hellojni_HelloJni_stringFromJNI()函數。
以上就是Android平台打包so和調用的一個最基本的demo,其實整個流程還是比較簡單的,有一些規定的命名是不能隨便修改的,如果jni檔案夾名,Android.mk,Application.mk檔案名稱,被java層調用的C函數命名等,這些都是有規則的。