1. 程式人生 > >Android 執行時資源替換----Runtime Resource Overlay

Android 執行時資源替換----Runtime Resource Overlay

       先拋一個問題:現在有一個第三方應用,沒有程式碼,只有編譯好的apk,在不去改動這個apk的前提下,如果想改變這個應用中的一些資源顯示效果,比如改變一個button的文字,一個imageview的背景,有沒有可能做到?

       我的答案是有可能做到(廢話,做不到的話讓我怎麼往下寫(bian))。直接上栗子,拿蘑菇街的應用來開刀。先看下蘑菇街的部分原生效果圖。

紅色框中的文字就是我要改變的地方,接下來放改變後的效果圖。

注意圈起來的紅色部分文字已經改變了,而我並沒有去改動mogujie的apk。不要問為啥替換後的文字這麼的喪心病狂,我會說我被mogujie傷害過嗎?

    上面的這個栗子就引出了我要講的Android 資源替換-----Runtime Resource Overlay(RRO)。RRO是在Android5.0後引入的,它能在 apk 執行時,自動載入需要定製的資源,而不載入原有的資源。mogujie的應用我壓根沒動,只是當系統在呼叫mogujie的資源的時候,我給它分配了一個同名的資源,這樣系統不會去獲取mogujie原生的資源,而獲取的是我分配給它的資源。給出了感性的一個解釋,下面開始講解具體的原理,再次之前需要先說明下應用查詢資源的過程。

    應用通過 getString/getDrawable去呼叫某個資源時,會將這個resources ID 作為引數傳給 framework 層。同一名稱但不同狀態的 resources ID 是一樣的,比如不同解析度但名稱相同的圖片分別被放置在了drawable-hdpi/drawable-ldpi/drawable-mdpi下,但在編譯時針對該圖片生成的resources ID只有一個。framework 層 查 找 資 源 時會使用這個resources ID , 同時還要使 用 當前 系 統 的configuration(解析度、語言、橫豎屏)。通過 resources ID 和 configuration,系統會找到最匹配的資源(如果沒有找到,則使用預設的 ),最後將找到的resources ID

返回給 apk。整個過程如下圖所示。



    結合上圖可以看出應用只是負責提供資源IDs,系統層會根據當前的語言,橫豎屏,解析度等狀態去取適當的資源ID載入。這種不連續性就提供了資源欺騙的可能,RRO發生在系統層檢索資源時,應用層使用資源的介面並沒有改變,甚至都不知道還有一個跟它同名的資源存在,因此使用RRO更改應用的資源時無需對原來的應用做任何修改。

     RRO機制的運用是依靠overlay apk實現的,與普通的apk相比它不包含程式碼,只有資源。一個overlay apk只能替換一個目標apk的資源,但一個目標apk的資源可以被多個overlay apk更改。在使用RRO機制的前提是必須要知道待替換目標apk中的資源名,比如我要替換mogujie在桌面的應用名稱,就要知道這個應用名稱是在哪個資原始檔中寫入的,這得依靠反編譯知識,這裡不詳述了,直接給出

有了這個就可以開始盡情的替換了。下面就是我製作的mogujie overlay 的目錄結構。

其中AndroidManifest.xml中的內容如下:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.mogujieoverylay">
    <overlay android:targetPackage="com.mogujie" android:priority="1"/>
</manifest>
targetPackage的值必須要與mogujie應用的package值一樣(反編譯可以獲取),priority的值是針對一個目標apk的資源可以被多個overlay apk更改"而設定的一個優先順序,值越大優先順序越高。

替換的幾個字串都是放在values目錄下的string.xml中

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">唯品會</string>
    <string name="host_tab_triple">不買</string>
    <string name="host_tab_follow">別逛</string>
    <string name="host_tab_msg">拒聊</string>
    <string name="host_tab_my">呵呵</string>
</resources>

這個過程的難度在於反編譯後,如何正確的猜測出目標apk中資源的名稱,只有拿到了正確的資源名才能保證overlay成功。到此打包編譯就生成了一個overlay apk。在羅嗦下打包的apk的方法.1在原始碼環境下使用Android.mk檔案然後進行模組化編譯,這個是最方便快捷的,我剛才使用的Android.mk內容如下

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := Mogujieoverlay
LOCAL_SRC_FILES := $(call all-java-files-under,res)
LOCAL_SDK_VERSION = current
LOCAL_PACKAGE_NAME := Mogujieoverlay
include $(BUILD_PACKAGE)

2使用aapt命令打包,然後簽名,必須要簽名,否則打包生成的apk是無法安裝的。

打包命令:

aapt p -f -I [ANDROID_SDK_PATH]/android.jar \
    -S [OVERLAY_PATH]/res \
    -M [OVERLAY_PATH]/AndroidManifest.xml \
    -c [PRODUCT_AAPT_CONFIG] \
    -F [MODULE_NAME]-overlay.apk

簽名命令:

java -jar [PATH]/signapk.jar \
    [PATH]/platform.x509.pem \
    [PATH]/platform.pk8 \
    [MODULE_NAME]-overlay.apk [MODULE_NAME]-overlay.apk_signed

    由於RRO是在系統層做的,Android出於安全考慮,要想overlay生效必須將生成的overlay apk放到/system/vendor/overlay目錄下才會生效。RRO的應用場景主要是在方案開發時用到,上面的例子只是替換了string資源,實際上RRO能支援除了 layout 和 AndroidManifest 外的所有資源定製,在做ROM定製開發時還是非常有用的。曾經測試報了一個google應用字串太長顯示不全的問題,但google 應用壓根就沒有程式碼,只有apk,當時就是靠RRO來解決該bug的。RRO剝離了程式碼和資源的共生關係,在方案開發中可以針對不同的顯示需求,打包不同的overlay apk,不用每次更改UI資源都去修改原來的應用。