1. 程式人生 > >android資原始檔overlay

android資原始檔overlay

aapt

aapt stands for Android Asset Packaging Tool. This tool is part of the SDK (and build system) and allows you to view, create, and update Zip-compatible archives (zip, jar, apk). It can also compile resources into binary assets.

aapt在android中主要打包資原始檔等,在definitions.mk中,aapt使用了-S PRIVATE_RESOURCE_DIR

去包含資原始檔,而PRIVATE_RESOURCE_DIR := $(LOCAL_RESOURCE_DIR),所以資原始檔都包含在LOCAL_RESOURCE_DIR中。

// build\core\definitions.mk
# This rule creates the R.java and Manifest.java files, both of which
# are PRODUCT-neutral.  Don't pass PRIVATE_PRODUCT_AAPT_CONFIG to this invocation.
define create-resource-java-files
@mkdir -p $(PRIVATE_SOURCE_INTERMEDIATES_DIR)
@mkdir -p $(dir $(PRIVATE_RESOURCE_PUBLICS_OUTPUT))
$(hide) $(AAPT) package $(PRIVATE_AAPT_FLAGS) -m \
    $(eval # PRIVATE_PRODUCT_AAPT_CONFIG is intentionally missing-- see comment.) \
$(addprefix -J , $(PRIVATE_SOURCE_INTERMEDIATES_DIR)) \ $(addprefix -M , $(PRIVATE_ANDROID_MANIFEST)) \ $(addprefix -P , $(PRIVATE_RESOURCE_PUBLICS_OUTPUT)) \ $(addprefix -S , $(PRIVATE_RESOURCE_DIR)) \ $(addprefix -A , $(PRIVATE_ASSET_DIR)) \ $(addprefix -I , $(PRIVATE_AAPT_INCLUDES)) \ $(addprefix -G , $(PRIVATE_PROGUARD_OPTIONS_FILE)) \ $(addprefix --min-sdk-version , $(PRIVATE_DEFAULT_APP_TARGET_SDK)) \ $(addprefix --target-sdk-version , $(PRIVATE_DEFAULT_APP_TARGET_SDK)) \ $(if
$(filter --version-code,$(PRIVATE_AAPT_FLAGS)),,$(addprefix --version-code , $(PLATFORM_SDK_VERSION))) \ $(if $(filter --version-name,$(PRIVATE_AAPT_FLAGS)),,$(addprefix --version-name , $(PLATFORM_VERSION))) \ $(addprefix --rename-manifest-package , $(PRIVATE_MANIFEST_PACKAGE_NAME)) \ $(addprefix --rename-instrumentation-target-package , $(PRIVATE_MANIFEST_INSTRUMENTATION_FOR)) endef

-S 引數用來指定資原始檔,搜尋多個目錄,從左到右,找到第一個匹配的

   -S  directory in which to find resources.  Multiple directories will be scanned
       and the first match found (left to right) will take precedence.

overlay的是資源

關於android中資源的定義http://developer.android.com/guide/topics/resources/index.html中有詳細的介紹,而overlay的主要作用就是為了替換apk中的資原始檔(隨便定義一個txt,並不能算資原始檔),例如,frameworks\base\core\res\res\values\strings.xml中就定義了很多framework中需要用到的資源(resources ,string),假設其中有4條資源,

//frameworks\base\core\res\res\values\strings.xml
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">

    <string name="byteShort">B</string>

    <string name="kilobyteShort">KB</string>

    <string name="megabyteShort">MB</string>

    <string name="gigabyteShort">GB</string>
</resources>    

在開發中,可以在其他地方定義PRODUCT_PACKAGE_OVERLAYS,新建一個strings.xml,修改其中的某個資源的的屬性值,

// 其他overlay目錄\frameworks\base\core\res\res\values\strings.xml
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
    <string name="gigabyteShort">gb</string>
</resources>    

這樣編譯完成後,gigabyteShort會變成修改後的值,其他3個值不變。

// overlay後的資原始檔
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">

    <string name="byteShort">B</string>

    <string name="kilobyteShort">KB</string>

    <string name="megabyteShort">MB</string>
    //這條變化了
    <string name="gigabyteShort">gb</string>
</resources>    

在這裡overlay替換的是資源,而不是檔案。所以即使新建的string.xml中僅有1條修改,最終編譯後的資原始檔中其他3條保持不變。

預設情況下,overlay目錄的資原始檔內容只能覆蓋原有軟體包中的資源,而不能新增資源。不然會造成編譯錯誤。
但是也有2種方法規避
1.原資原始檔中也新增上該資源(這樣就不算新增了),
2.一種更加簡便的方法是給aapt命令增加–auto-add-overlay選項。

overlay的也可能是檔案

最近在修改frameworks\base\core\res\res\xml\power_profile.xml中電池總電量的數值,將battery.capacity修改為3500mA,下面是對應的overlay檔案,只添加了一條。

<device name="Android">
<item name="battery.capacity">3500</item>
</device>

但是修改完成後發現問題,設定—電池—上次充滿後的電量使用情況一直是沒有電池使用情況。檢視相關程式碼發現,
只有在power_profile.xml中的screen.full大於10才會去refresh電量使用情況。

// packages\apps\Settings\src\com\android\settings\fuelgauge\PowerUsageSummary.java

  private void refreshStats() {

        final PowerProfile powerProfile = mStatsHelper.getPowerProfile();
        final BatteryStats stats = mStatsHelper.getStats();
        // power profile就是上面的power_profile.xml
        // screen.full
        final double averagePower = powerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL);
        // MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP = 10;
        // screen.full所對應的值必須大於10才會去refresh電量使用情況
        if (averagePower >= MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP) {
       ...........
        }
        if (!addedSome) {
            addNotAvailableMessage();
        }

        BatteryEntry.startRequestQueue();
    }

而out目錄下生成的資原始檔中,power_profile.xml只有battery.capacity這一條(沒有對應的screen.full這條,所以才不會去顯示電量使用情況),所以這時候也可以說是檔案覆蓋了,仔細檢視原power_profile.xml中的內容,發現其格式並不是android所規定的資原始檔,而是一個特殊的xml檔案,這時候就不是overlay資源了,而是類似於替換檔案。
同時檢視frameworks\base\core\res\res\xml\power_profile.xml原生的引數值,發現screen.full為0.1,也不是大於10,為何在沒新增power_profile.xml這個overlay檔案時,功能一切正常?

<item name="screen.full">0.1</item>

搜尋整個程式碼(高通8939平臺),發現DEVICE_PACKAGE_OVERLAYS中有對power_profile.xml的overlay,其中是高通自己定義的一些電池的引數,其中screen.full為261。

// device\qcom\msm8916_64\msm8916_64.mk
DEVICE_PACKAGE_OVERLAYS := device/qcom/msm8916_64/overlay

那麼既然高通本身有對power_profile.xml的overlay,自己新增的overlay將高通的覆蓋了?在package_internal.mk中,

 // build\core\package_internal.mk中
 // PRODUCT_PACKAGE_OVERLAYS排在DEVICE_PACKAGE_OVERLAYS前面,
 // 所以PRODUCT_PACKAGE_OVERLAYS 優先於 DEVICE_PACKAGE_OVERLAYS
 package_resource_overlays := $(strip \
    $(wildcard $(foreach dir, $(PRODUCT_PACKAGE_OVERLAYS), \
      $(addprefix $(dir)/, $(LOCAL_RESOURCE_DIR)))) \
    $(wildcard $(foreach dir, $(DEVICE_PACKAGE_OVERLAYS), \
      $(addprefix $(dir)/, $(LOCAL_RESOURCE_DIR)))))

LOCAL_RESOURCE_DIR := $(package_resource_overlays) $(LOCAL_RESOURCE_DIR)

從上面可以看出,PRODUCT_PACKAGE_OVERLAYS 優先於 DEVICE_PACKAGE_OVERLAYS,而aapt -S會從左到右搜尋資原始檔,所以PRODUCT_PACKAGE_OVERLAYS中定義的會優先被匹配
所以最終的修改方法是:
1.將高通DEVICE_PACKAGE_OVERLAYS中的power_profile.xml拷貝一份到自己的overlay目錄中(電池等的引數,不同的裝置廠商的引數不同,因此以裝置廠商的為準,而不用原生的power_profile.xml),
2.在此power_profile.xml的基礎上新增或者修改其中的引數。