1. 程式人生 > >android原始碼編譯apk整合第三方so庫

android原始碼編譯apk整合第三方so庫

一般編譯android應用,生成apk,有以下幾個方法:

    1.傳統的在eclipse平臺下編譯生成apk,這種情況下,需要指定android sdk,通過eclipse編譯生成apk,編譯的apk擁有較低級別的使用者級別許可權,可能有些底層操作沒有許可權實現,比如部分system呼叫,訪問系統檔案目錄等。如果存在jni呼叫,則需要建立jni目錄,並在jni目錄裡面建立android.mk,通過ndk編譯生成動態庫.so供上層呼叫,生成的動態庫必須放在libs/armeabi或者libs/armeabli-v7a目錄下面,在eclipse打包時候才能將.so打包到apk中,安裝此apk,會安裝到/data/app目錄下,同時將apk和so檔案寫入/data/相應目錄下,表示安裝成功。

    2.如果需要生成apk擁有system許可權,則需要在AndroidManifest.xml中指定android.sharedUserId="android.uid.system"裡獲取system許可權,同時在生成apk後要進行簽名,簽名命令類似如下格式:

    java -jar signapk.jar platform.x509.pem platform.pk8 old.apk new.apk 

  這樣生成的apk會比1中生成的apk擁有較高級別的許可權。platform.x509.pem platform.pk8這兩個檔案一般在所在android原始碼系統中的build\target\product\security目錄下面,signapk.jar為一個簽名工具,一般在原始碼

out/hostlinux-x86/framework/signapk.jar 目錄下面。

  3.無論1和2,android應用均只能呼叫開放的SDK中api或者屬性引數,由於SDK是android framework的一個子集,有大量framework中的api或者屬性並沒有開放給SDK,因此android上層應用無法呼叫。如果想呼叫framework中的一些未開放到SDK的api,同時要求apk擁有系統級許可權,那麼只能將android應用放到原始碼裡進行編譯。我將android應用放到android原始碼中的packages/apps/中,通過mmm命令來編譯,這些應用可以呼叫framework的api並擁有系統級許可權,生成的apk在out/target/product/dt307sq/system/app/下,dt307sq是硬體平臺名稱。packages/apps/下有大量系統級應用,這些應用在原始碼編譯的時候會隨原始碼一起編譯,隨系統一起安裝,安裝在系統/system/app下,這些系統級應用可以作為我們的參考。對於牽涉到了jni呼叫的情況,同方法1,必須編譯出.so庫,才能編譯成功。比如原始碼裡面有個Bluetooth的例子,其目錄結構為


Android.mk內容如下:

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)


LOCAL_MODULE_TAGS := optional


LOCAL_SRC_FILES := \
        $(call all-java-files-under, src)


LOCAL_PACKAGE_NAME := Bluetooth
LOCAL_CERTIFICATE := platform


LOCAL_JNI_SHARED_LIBRARIES := libbluetooth_jni
LOCAL_JAVA_LIBRARIES := javax.obex
LOCAL_STATIC_JAVA_LIBRARIES := com.android.vcard


LOCAL_REQUIRED_MODULES := libbluetooth_jni bluetooth.default


LOCAL_PROGUARD_ENABLED := disabled


include $(BUILD_PACKAGE)


include $(call all-makefiles-under,$(LOCAL_PATH))

LOCAL_JNI_SHARED_LIBRARIES := libbluetooth_jni表示該apk依賴libbluetooth_jni這個模組,這個模組的編譯指令碼在jni目錄下:

LOCAL_PATH := $(call my-dir)


include $(CLEAR_VARS)


LOCAL_SRC_FILES:= \
    com_android_bluetooth_btservice_AdapterService.cpp \
    com_android_bluetooth_hfp.cpp \
    com_android_bluetooth_a2dp.cpp \
    com_android_bluetooth_hid.cpp \
    com_android_bluetooth_hdp.cpp \
    com_android_bluetooth_pan.cpp


LOCAL_C_INCLUDES += \
    $(JNI_H_INCLUDE) \


LOCAL_SHARED_LIBRARIES := \
    libandroid_runtime \
    libnativehelper \
    libcutils \
    libutils \
    libhardware


#LOCAL_CFLAGS += -O0 -g


LOCAL_MODULE := libbluetooth_jni
LOCAL_MODULE_TAGS := optional


include $(BUILD_SHARED_LIBRARY)

這個生成libbluetooth_jni.so

這樣通過mmm編譯的apk包含進了.so庫。

但有這樣一種情況,.so庫是在外面通過android ndk編譯生成的,沒有在原始碼裡面編譯,這個就是第三方庫,那麼如何將.so庫打到apk裡面呢。剛開始,我想到一個辦法。將.so檔案放到/lib/armeabi/這個目錄裡面,然後再通過mmm編譯生成apk,但發現apk裡面根本沒有lib這個目錄。後來我又嘗試另外一種方法:

改下android.mk如下:

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)


LOCAL_STATIC_JAVA_LIBRARIES:= xstream
LOCAL_STATIC_LIBRARIES:= libnative-backendservice-jni
LOCAL_MODULE_TAGS := optional


LOCAL_SRC_FILES += $(call all-java-files-under, src)
LOCAL_SRC_FILES += $(call all-java-files-under,gen)


LOCAL_PACKAGE_NAME := ClientAgent
LOCAL_CERTIFICATE := platform


#LOCAL_PROGUARD_NEALBED: = false
#LOCAL_PROGUARD_FLAG_FILES := proguard.cfg


include $(BUILD_PACKAGE)


include $(CLEAR_VARS)
LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES:=xstream:lib/xstream-1.4.4.jar
include $(BUILD_MULTI_PREBUILT)


include $(CLEAR_VARS)
LOCAL_PREBUILT_LIBS:=libnative-backendservice-jni:lib/armeabi/libnative-backendservice-jni.so
include $(BUILD_MULTI_PREBUILT)




# Use the folloing include to make our test apk.
#include $(call all-makefiles-under,$(LOCAL_PATH))

這種方法通過include $(BUILD_MULTI_PREBUILT)整合第三方庫,但發現這種方法只是在編譯apk的時候,將第三方庫自動拷貝到out/target/product/dt307sq/system/lib下,根本沒有在apk裡面打包第三方.so庫。

最終發現以下辦法能將第三方庫打包到原始碼編譯的apk中。

1).將第三方庫.so放到應用下的lib/armeabi中

2).通過mmm編譯出apk,這個apk裡面沒有包含lib目錄

3).通過aapt命令,新增lib/armeabi裡的.so庫,例如:./aapt a ../../../out/target/product/dt307sq/system/app/ClientAgent.apk lib/armeabi/libnative-backendservice-jni.so

aapt命令在原始碼out/host/linux-x86/bin下,是很強大的工具,注意一定要帶lib/armeabi/目錄,apk會根據名稱生成對應的目錄

4).最後簽名,例如:java -jar signapk.jar platform.x509.pem platform.pk8 ../../../out/target/product/dt307sq/system/app/ClientAgent.apk ClientAgentSign.apk

生成的apk,具有系統級許可權,在原始碼裡編譯生成的,同時需要呼叫的jni動態庫是在原始碼外面通過android ndk編譯生成的。

一般情況下,需要在原始碼裡編譯,最好android應用和jni動態庫部分都通過原始碼來編譯,這是最好的方法。但是有些情況下,需要呼叫大量第三方庫,因為原始碼編譯環境下,這些第三方庫不一定能編譯通過,那就只能在外面編譯好了,然後加到原始碼編譯生成的apk中。