一 簡介
Android NDK的構(gòu)建系統(tǒng)是基于GUN Makefile的,Android.mk其實(shí)就是一個(gè)用于向Android NDK構(gòu)建系統(tǒng)描述NDK項(xiàng)目的GUN Makefile片段。
在編寫涉及JNI的項(xiàng)目時(shí),往往需要自己配置Android.mk:主要通過makefile語法,使用變量或語句對(duì)用到的工具鏈、要編譯生成的庫名稱、生成庫所依賴的源文件、生成動(dòng)態(tài)庫或靜態(tài)庫等信息進(jìn)行描述...
下面從變量開始認(rèn)識(shí)Android.mk
二 Android.mk中的常用變量
1、LOCAL_PATH
LOCAL_PATH :=$(call my-dir)
Android構(gòu)建系統(tǒng)利用LOCAL_PATH
來定位源文件,Android構(gòu)建系統(tǒng)要求,Android.mk文檔必須以該變量的定義開頭。
my-dir
是宏功能,這里將該變量設(shè)置為my-dir
的返回值,即設(shè)置為當(dāng)前目錄
my-dir
就是該Android.mk所在目錄,一般就是我們的jni目錄
2、Include &(CLEAR_VARS)
Android構(gòu)建系統(tǒng)在單次執(zhí)行中解析多個(gè)構(gòu)建文件,
LOCAL_<name>
是全局變量,這條語句在每個(gè)模塊開頭使用,可以幫助清除 除了LOCAL_PATH
以外的其他LOCAL_<name>
變量,避免模塊間變量沖突,
例如可以清除如下文件:LOCAL_MODULE
、LOCAL_SRC_FILES
3、LOCAL_MODULE
LOCAL_MODULE
變量,簡單理解就是給生成的so庫文件設(shè)置的名稱,如:
LOCAL_MODULE : = hello-jni
,表示生成名為hello-jni
的模塊,
因?yàn)槟K名稱也被用于給構(gòu)建過程生成的文件命名,
因此,構(gòu)建系統(tǒng)加上適當(dāng)?shù)那熬Y,最終生成libhello-jni.so
4、LOCAL_SRC_FILES
該變量定義了生成庫文件的源文件列表:
LOCAL_SRC_FILES := hello-jni.cpp
這里hello-jni只由一個(gè)源文件生成,包含多個(gè)源文件,注意用空格分開:
LOCAL_SRC_FILES := hello-jni.cpp hello-ndk.cpp
5、LOCAL_C_INCLUDES:
表示頭文件的搜索路徑,用來指定在編譯時(shí)即將使用的頭文件的位置,
一般在使用預(yù)編譯庫構(gòu)建so時(shí)使用,用于包含第三方源文件對(duì)應(yīng)的頭文件
例:
LOCAL_C_INCLUDES := $(LOCAL_PATH) \
$(ClientCommon_Path)/basecore/a2/framecore/include \
$(ClientCommon_Path)/include/android/client \
$(ClientCommon_Path)/basecore/a2/appshare/include/ \
$(ClientCommon_Path)/basecore/a2/avcore/include/avcore \
$(ClientCommon_Path)/include/android/meetingcore \
$(ClientCommon_Path)/include/android/uithreadhandler \
$(ClientCommon_Path)/include/android/MultiWhiteBoard \
$(ClientCommon_Path)/include/android/terminalonlinemanager \
$(ClientCommon_Path)/protocol
6、LOCAL_LDLIBS:
用于告訴鏈接器,生成的模塊要在加載時(shí)鏈接到的庫,表示編譯模塊時(shí)要使用的附加的鏈接器選項(xiàng)。
使用‘-l’前綴傳遞指定庫的名字
例:
LOCAL_LDLIBS := -lz
表示告訴鏈接器生成的模塊要在加載時(shí)鏈接到
/system/lib/libz.so
或/system/lib/libz.a
(可鏈接到動(dòng)態(tài)庫或靜態(tài)庫)
例:
要與NDK日志庫鏈接:
LOCAL_LDLIBS:= -llog
連接到多個(gè)靜態(tài)庫或動(dòng)態(tài)庫(用空格隔開):
LOCAL_LDLIBS:= -ltinyxml32 -luithreadhandler -lxmlprotocolparser32
7、 自定義新的變量
MyLibrary_Path := $(LOCAL_PATH)/../../library
( ../ 表示上一級(jí)目錄)
注意:以LOCAL和NDK前綴開頭的名稱預(yù)留給Android NDK構(gòu)建系統(tǒng)使用,自定義變量應(yīng)避免使用這兩個(gè)前綴
三 構(gòu)建動(dòng)態(tài)庫(xxx.so)
為了建立可供主應(yīng)用程序使用的模塊,必須將該模塊變成動(dòng)態(tài)庫,只有動(dòng)態(tài)庫才能被安裝/復(fù)制到APK包中。
構(gòu)建動(dòng)態(tài)庫,在Android.mk中相應(yīng)模塊下添加如下描述語句即可:
include &(BUILD_SHARED_LIBRARY)
比如在一個(gè)工程中定義兩個(gè)動(dòng)態(tài)庫:
LOCAL_PATH := &(call my-dir)
//模塊1:
include &(CLEAR_VARS)
LOCAL_MODULE := module1
LOCAL_SRC_FILES := moudel1.cpp
include &(BUILD_SHARED_LIBRARY)
----------------------------------------------------
//模塊2:
include &(CLEAR_VARS)
LOCAL_MODULE := module2
LOCAL_SRC_FILES := moudel2.cpp
include &(BUILD_SHARED_LIBRARY)
定義完成后,NDK構(gòu)建系統(tǒng)將會(huì)產(chǎn)生兩個(gè)動(dòng)態(tài)庫:moudel1.so
、moudel2.so
四 構(gòu)建靜態(tài)庫(xxx.a)
構(gòu)建靜態(tài)庫,在Android.mk相應(yīng)模塊添加描述語句:
include $(BUILD_STATIC_LIBRARY)
(生成靜態(tài)庫)
Android應(yīng)用程序不直接使用靜態(tài)庫,且apk中也不包含靜態(tài)庫,但靜態(tài)庫可以用來構(gòu)建動(dòng)態(tài)庫
例如:
開放某個(gè)核心庫的功能給他人使用時(shí),無須將核心源代碼提供給客戶,而是將這些代碼編譯成靜態(tài)庫,用戶只需并入他們的動(dòng)態(tài)庫就可使用了
//編譯.a靜態(tài)庫
LOCAL_PATH := &(call my-dir)
include &(CLEAR_VARS)
LOCAL_MODULE := avilib
LOCAL_SRC_FILES := avilib.cpp platform.cpp
include &(BUILD_STATIC_LIBRARY) //編譯生成靜態(tài)庫
第三方使用這個(gè)靜態(tài)庫構(gòu)建自己的動(dòng)態(tài)庫在應(yīng)用程序中使用:
用庫名稱為變量LOCAL_STATIC_LIBRARY賦值,即可引用靜態(tài)庫:
LOCAL_STATIC_LIBRARY:靜態(tài)庫名稱
//原生模塊
LOCAL_PATH := &(call my-dir)
include &(CLEAR_VARS)
LOCAL_MODULE := module2
LOCAL_SRC_FILES := module2.cpp
LOCAL_STATIC_LIBRARIES := avilib //引用編譯好的.a靜態(tài)庫
include &(BUILD_SHARED_LIBRARY)
五 用動(dòng)態(tài)庫構(gòu)建通用模塊
承接上一個(gè)靜態(tài)庫的示例代碼,多個(gè)動(dòng)態(tài)庫與同一個(gè)靜態(tài)庫相連時(shí),會(huì)產(chǎn)生重復(fù)連接,增加了apk大小
所以,當(dāng)有多個(gè)動(dòng)態(tài)庫都使用某個(gè)庫時(shí),一般將該模塊建立為動(dòng)態(tài)庫,避免重復(fù)連接。
LOCAL_PATH := &(call my-dir)
//編譯動(dòng)態(tài)庫
include &(CLEAR_VARS)
LOCAL_MODULE := avilib
LOCAL_SRC_FILES := avilib.cpp platform.cpp
include &(BUILD_SHARED_LIBRARY) //多個(gè)moduel使用,構(gòu)建成動(dòng)態(tài)庫
多個(gè)模塊引用同一個(gè)動(dòng)態(tài)庫:
使用變量LOCAL_SHARED_LIBRARIES引用動(dòng)態(tài)庫:
LOCAL_SHARED_LIBRARIES :動(dòng)態(tài)庫名稱
//原生模塊1
include &(CLEAR_VARS)
LOCAL_MODULE := module1
LOCAL_SRC_FILES := module1.cpp
LOCAL_SHARED_LIBRARIES := avilib //引用1
include &(BUILD_SHARED_LIBRARY)
----------------------------------------------------
//原生模塊2
include &(CLEAR_VARS)
LOCAL_MODULE := module2
LOCAL_SRC_FILES := module2.cpp
LOCAL_SHARED_LIBRARIES := avilib //引用2
include &(BUILD_SHARED_LIBRARY)
六 使用Rebuild庫完成預(yù)構(gòu)建
Prebuid:預(yù)構(gòu)建
NDK R5以后包含Rebuid庫,在Rebuild庫的支持下,可以依賴編譯好的so生成動(dòng)態(tài)庫
1、聲明預(yù)編譯庫的模塊
Android.mk可以寫成如下形式:
LOCAL_PATH := &(call my-dir)
COMMON_LIBS_PATH := ../../ClientCommon
// 預(yù)構(gòu)建庫
include &(CLEAR_VARS)
LOCAL_MODULE := avnet //生成的預(yù)編譯庫名稱(名稱可不與依賴庫相同)
LOCAL_SRC_FILES := $(COMMON_LIBS_PATH)/basecore/libavnet.so
include &(PREBUILT_SHARED_LIBRARY)
說明:
1)LOCAL_SRC_FILES
變量不再指向源文件,而是直接指向so庫文件
2)預(yù)構(gòu)建庫的聲明語句:
include &(PREBUILT_SHARED_LIBRARY)
生成預(yù)構(gòu)建動(dòng)態(tài)庫
include &(PREBUILT_STATIC_LIBRARY)
生成預(yù)構(gòu)建靜態(tài)庫
2、在其他模塊引用這個(gè)預(yù)編譯庫:
與引用普通動(dòng)態(tài)庫的方式相同
預(yù)編譯靜態(tài)庫的名字賦值給變量LOCAL_STATIC_LIBRARIES
預(yù)編譯動(dòng)態(tài)庫的名字賦值給變量LOCAL_SHARED_LIBRARIES
3、將預(yù)編譯庫的頭文件導(dǎo)出:LOCAL_C_INCLUDES
得到預(yù)編譯庫后,一般需要提供它對(duì)應(yīng)的頭文件,
因?yàn)橐蕾噹焐深A(yù)編譯庫時(shí),需要將它的頭文件提供給NDK構(gòu)建系統(tǒng)
例:
依賴libavnet.so
生成預(yù)編譯庫時(shí),有對(duì)應(yīng)的avnet.h頭文件在include
子目錄,
那么在Android.mk中需要使用LOCAL_C_INCLUDES
變量指定該頭文件或直接指定該目錄:
LOCAL_C_INCLUDES := $(LOCAL_PATH) /include
4、優(yōu)點(diǎn)
利用Prebuild預(yù)編譯,依賴so庫生成最終的so庫
1)可不發(fā)布源代碼,只將so庫發(fā)布給第三方開發(fā)人員
2)直接指向so庫文件,稱之為預(yù)構(gòu)建,可以加速構(gòu)建過程
七 條件操作
針對(duì)不同的平臺(tái),可能使用不同的代碼,使用條件操作可進(jìn)行區(qū)分處理
例:
ifeq (&(TARGET_ARCH),arm)
LOCAL_SRC_FILES += armonly.c
else
LOCAL_SRC_FILES += generic.c
endif