Android 系統預置 APP 是做 Framework 應用開發經常經常會遇到的工作,預置 APP 分為兩種,一種是直接預置 APK,一種是預置帶有源碼的 APP。
預置 apk
示例說明
以 Shadowsocks.apk 示例,在 AOSP/packages/apps 新建名為 Shadowsocks 的文件,放入 Shadowsocks.apk,再新建 Android.mk,內容如下:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := < your app folder name >
# 簽名
LOCAL_CERTIFICATE := < desired key >
# 指定 src 目錄
LOCAL_SRC_FILES := < app apk filename >
LOCAL_MODULE_CLASS := APPS
# 該模塊的后綴,不用定義
#LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
include $(BUILD_PREBUILT)
解釋:
LOCAL_PATH := $(call my-dir)
每個 Android.mk 文件必須以定義 LOCAL_PATH 為開始,它用於在開發 tree 中查找源文件。
include $(CLEAR_VARS)
CLEAR_VARS 變量由 Build System 提供,並指向一個指定的 GNU Makefile,由它負責清理很多 LOCAL_xxx。
例如:LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES 等等,但不清理 LOCAL_PATH。
LOCAL_MODULE_TAGS := user eng tests optional
可選定義,表示在什么版本情況下編譯該版本,默認 optional
user: 指該模塊只在 user 版本下才編譯
eng: 指該模塊只在 eng 版本下才編譯
tests: 指該模塊只在 tests 版本下才編譯
optional:指該模塊在所有版本下都編譯
- LOCAL_MODULE
模塊名,可不用定義,默認 = $(LOCAL_PACKAGE_NAME),不能和既有模塊相同,如果該變量未設置,則使用 LOCAL_PACKAGE_NAME,如果再沒有,就會編譯失敗。
- LOCAL_CERTIFICATE
在什么情況下簽名。
testkey:普通 APK,默認情況下使用。
platform:該 APK 完成一些系統的核心功能。經過對系統中存在的文件夾的訪問測試,
這種方式編譯出來的 APK 所在進程的 UID 為 system,可以參見 Settings。
shared:該 APK 需要和 home/contacts 進程共享數據,可以參見 Launcher。
media:該 APK 是 media/download 系統中的一環,可以參見 Gallery。
- LOCAL_MODULE_CLASS
指定模塊的類型,可不用定義。
# 編譯 apk 文件
LOCAL_MODULE_CLASS := APPS
# 編譯 jar 包
LOCAL_MODULE_CLASS := JAVA_LIBRAYIES
# 定義動態庫文件
LOCAL_MODULE_CLASS := SHARED_LIBRAYIES
# 編譯可執行文件
LOCAL_MODULE_CLASS := EXECUTABLES
include $(BUILD_PACKAGE)
表示生成一個 apk,它可以是多種類型
BUILD_PACKAGE(既可以編apk,也可以編資源包文件,但是需要指定LOCAL_EXPORT_PACKAGE_RESOURCES:=true)
BUILD_JAVA_LIBRARY(java共享庫)
BUILD_STATIC_JAVA_LIBRARY(java靜態庫)
BUILD_EXECUTABLE(執行文件)
BUILD_SHARED_LIBRARY(native共享庫)
BUILD_STATIC_LIBRARY(native靜態庫)
完整示例
Shadowsocks.apk 對應如下:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := Shadowsocks
# 系統簽名
LOCAL_CERTIFICATE := PRESIGNED
LOCAL_SRC_FILES := $(LOCAL_MODULE).apk
LOCAL_MODULE_CLASS := APPS
#LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
include $(BUILD_PREBUILT)
更改 device.mk 文件
AOSP/build/target/board/lunch的版本/device.mk 文件,我編的是 aosp_x86-eng,所以增加或者更新 AOSP/build/target/board/generic_x86/device.mk:
PRODUCT_PACKAGES += \
Shadowsocks \
使用 mmm 命令來編譯指定的模塊:
mmm packages/apps/Shadowsocks
編譯好模塊后,還要重新打包一下 system.img 文件:
make snod
完成后就可以燒錄了。
問題
1、如何將 APK 預置到 system/priv-app 里?
加入 priv-app 方法:在 Android.mk 中增加 LOCAL_PRIVILEGED_MODULE := true
預置有源碼 APP
預置有源碼 APP 比預置 APK 要麻煩很多,可能會涉及 jar 包和 so 庫等。現在基本都是基於 Android Studio 的項目 MyTestProject1,我們先在 AOSP/packages/apps 新建名為 MyTestProject2 的文件夾,在新建 MyTestProject2/libs、MyTestProject2/res、MyTestProject2/src,分別將 MyTestProject1 下 jar 包和 so 庫拷到 MyTestProject2/libs 和 MyTestProject2/libs/armeabi,將 MyTestProject1/app/src/main/res 拷到 MyTestProject2/res,將 MyTestProject1/app/src/main/java 下文件拷到 MyTestProject2/src 下。
引用第三方 jar 包
假設,我們當前目錄下的 libs 有 AndroidUtil.jar包,我們想引用它,需要做兩個步驟:
第一步、 聲明我們 jar 包所在的目錄
LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := AndroidUtil:libs/AndroidUtil.jar
這行代碼的意思大概可以理解成這樣,聲明一個變量 AndroidUtil,它的 value 是 libs/AndroidUtil.jar
include $(CLEAR_VARS)
LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := AndroidUtil:libs/AndroidUtil.jar
include $(BUILD_MULTI_PREBUILT)
第二步、 引用我們聲明 jar 包的變量
include $(CLEAR_VARS)
# 省略其他
LOCAL_STATIC_JAVA_LIBRARIES := \
AndroidUtil
# 省略其他
include $(BUILD_PACKAGE)
引用 so 庫
假設,我們當前目錄下的 libs/armeabi 有 libBaiduMapSDK1.so、libBaiduMapSDK1.so,libs/arm64-v8a 有 libBaiduMapSDK1.so、libBaiduMapSDK1.so,我們想引用它,有兩種方法,可以在根目錄 Android.mk 引用 so 庫,也可以在 libs 下再建個 Android.mk 配置好 so 庫,然后 include,推薦第二種方式。
libs/Android.mk
#====================================================
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_SUFFIX := .so
LOCAL_MODULE := libBaiduMapSDK1
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
LOCAL_SRC_FILES_arm :=libs/armeabi/$(LOCAL_MODULE).so
LOCAL_SRC_FILES_arm64 :=libs/arm64-v8a/$(LOCAL_MODULE).so
LOCAL_MODULE_TARGET_ARCHS:= arm arm64
LOCAL_MULTILIB := both
include $(BUILD_PREBUILT)
#====================================================
#====================================================
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_SUFFIX := .so
LOCAL_MODULE := libBaiduMapSDK2
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
LOCAL_SRC_FILES_arm :=libs/armeabi/$(LOCAL_MODULE).so
LOCAL_SRC_FILES_arm64 :=libs/arm64-v8a/$(LOCAL_MODULE).so
LOCAL_MODULE_TARGET_ARCHS:= arm arm64
LOCAL_MULTILIB := both
include $(BUILD_PREBUILT)
引用 so 庫
include $(CLEAR_VARS)
# 省略其他
LOCAL_JNI_SHARED_LIBRARIES := \
libBaiduMapSDK1 \
libBaiduMapSDK2
# 省略其他
include $(BUILD_PACKAGE)
##########引用第三方 so 庫##########
include $(LOCAL_PATH)/libs/Android.mk
完整示例
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_PACKAGE_NAME := TestName
LOCAL_CERTIFICATE := platform
# 引入系統資源文件
LOCAL_USE_AAPT2 := true
# Java文件
LOCAL_SRC_FILES := $(call all-java-files-under, src)
# 資源文件,可選定義,推薦不定義
#LOCAL_RESOURCE_DIR = \
# $(LOCAL_PATH)/res \
# frameworks/support/v7/appcompat/res \
# frameworks/support/design/res
# 可以使用系統 hide api
LOCAL_PRIVATE_PLATFORM_APIS := true
# 導入系統依賴
LOCAL_STATIC_ANDROID_LIBRARIES := \
android-support-design \
android-support-v4 \
android-support-v7-appcompat \
android-support-v7-recyclerview
LOCAL_STATIC_JAVA_LIBRARIES := \
AndroidUtil
LOCAL_JNI_SHARED_LIBRARIES := \
libBaiduMapSDK1 \
libBaiduMapSDK2
# R資源生成別名,--extra-packages 是為資源文件設置別名:意思是通過該應用包名+R,com.android.test1.R 和 com.android.test2.R 都可以訪問到資源
LOCAL_AAPT_FLAGS := --auto-add-overlay
LOCAL_AAPT_FLAGS += --extra-packages android.support.v4
LOCAL_AAPT_FLAGS += --extra-packages android.support.v7.appcompat
LOCAL_AAPT_FLAGS += --extra-packages android.support.design
LOCAL_AAPT_FLAGS += --extra-packages android.support.v7.recyclerview
# 制定編譯的工程,不要使用代碼混淆的工具進行代碼混淆
LOCAL_PROGUARD_ENABLED := disabled
# 指定不需要混淆的native方法與變量的proguard.flags文件
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
include $(BUILD_PACKAGE)
##########引用第三方 jar 包##########
include $(CLEAR_VARS)
LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := AndroidUtil:libs/AndroidUtil.jar
include $(BUILD_MULTI_PREBUILT)
##########引用第三方 so 庫##########
include $(LOCAL_PATH)/libs/Android.mk
問題
1、LOCAL_PRIVATE_PLATFORM_APIS 和 LOCAL_SDK_VERSION 有什么區別?
LOCAL_PRIVATE_PLATFORM_APIS := true
設置后,會使用 sdk 的 hide 的 api 來編譯。
LOCAL_SDK_VERSION 這個編譯配置,就會使編譯的應用不能訪問 hide 的 api,有時一些系統的 class 被 import 后編譯時說找不到這個類,就是這個原因造成的。
2、如果直接用 mmm 編譯然后 adb install -r xxx.apk 大概會出現如下錯誤:
Failed to install out/target/product/p212/system/app/xxx/xxx.apk: Failure [INSTALL_FAILED_INVALID_APK: Package couldn't be installed in /data/app/com.droidlogic.mboxlauncher-1: Package /data/app/com.droidlogic.mboxlauncher-1/base.apk code is missing]
解決方法:
在對應 app 的 Android.mk 文件中加入
LOCAL_DEX_PREOPT := false
關閉 dex 優化來提高調試過程,把編譯后的 APK 直接替換安裝 adb install -r XXX.apk,不然 APK 得 Push 到 system/app,重啟設備。
3、在 Android Studio Gradle 方式中通過 implementation 方式加載的三方庫,並沒有下載 jar 文件放到 libs 文件夾下啊,該如何集成?
其實 jar 包有被下載到項目的 External Libraries 目錄下,找到引用的 jar 包,點右鍵 Show in Files,就能得到了 jar 包的文件地址,然后把它拷到 libs 文件夾下,就能像別的 jar 包一樣處理了。
另外在 External Libraries 目錄還能看到隱藏的 jar,比如 retrofit,其實它有引用 okhttp,okhttp 又引用了 okio,這些也是需要的,一並拷到 libs 文件夾下。
4、第三方無法定位?
第三方定位如百度、高德,申請 SDK 時會需要填寫包名和打包簽名等信息,如何正確地預置源碼 APP 可以定位,除了配置LOCAL_CERTIFICATE := platform
使用系統的簽名,還得在項目的AndroidMainfest.xml 根節點配置android:sharedUserId="android.uid.system"
。
公眾號
我的公眾號:吳小龍同學,歡迎關注交流~