[Android] Runtime Resource Overlay 遇到的大小事

Timothy Chung
6 min readSep 6, 2019

--

起源:

我們的APP裡面有些部分是google GMS的頁面,基本上這些頁面是沒辦法做客製的只能從他給定的幾個的theme中選一個套用,而套用的方式為:塞一個name為theme_type的String到values/strings.xml中,字串內容則為他給定的theme名稱。

之所以會去研究這件事情,是因為被指派了一個需求:我們的APP需要在某些狀況下換theme,自己的部分換theme很簡單,但GMS部分的theme卻是寫在resource中,所以在runtime中換置換那個叫做"theme_type"的字串內容就變成主要需求。

試過這個方法:https://stackoverflow.com/a/42936357/4473021
但基本上是沒效的,後來才發現有RRO這個機制

RRO是什麼:

是一個Android 5.0後出現的機制,允許你可以在不用修改某隻app(A app)code的狀況下,以安裝另一隻overlay apk(B app)的方式,影響framework回傳resource ID的導向機制,即是:A app本來應該要取用某些resource內容時,會被自動導向去取用B app中的內容。

大致上是怎樣:

就是寫一隻overlay APP,裡面不需要有src/部分,只需要有res/的部分,並在res裡面用與原APP中相同的name去定義resource(strings, colors…etc),接著把這隻apk push進機台裡面。

用個例子開始吧:

1. overlay target app

首先先製作一個等一下需要被覆蓋的APP,簡單寫一個只有一個button的activity的APP:

最後app大概會長這樣:

原先的長相

2.overlay app

寫另一隻APP,企圖改掉button的內容,app的background color

可以注意到的是:

  • AndroidManifest.xml中 overlay tag裡, android:targetPackage要填上覆蓋目標app的packagename;android:priority代表若存在多個overlay時的優先權(對,可以對同一個app做多個覆蓋,我覆蓋你的覆蓋)
  • colors.xml和strings.xml中的name,則需要和目標app中的name一致才能完成覆蓋。你可能會想:如果我要覆蓋非系統原生的資源(system UI)或是像前言所提是自己的app,不知道resource name怎辦?這時候就是反組譯出場的時候喽
  • 這支APP裡面沒有src/

結構如下:

app結構

3.build overlay apk

做這點小事就沒用Android Studio了,我是用aapt打包apk並用signapk.jar去做signkey的動作,貼一下我的指令:

> 打包$ aapt package -f -M AndroidManifest.xml -S res/ -I ~/Android/Sdk/platforms/android-28/android.jar -F myOverlays.apk
> sign key$java -jar signapk.jar platform.x509.pem platform.pk8 myOverlays.apk myOverlays_signed.apk

聰明的同學可以忽略不要看。

4.安裝overlay app

說是安裝,但需要用push的,push到機台中的/system/vendor/overlay/

首先


$ adb root
$ adb remount
//創建overlay的資料夾
$ adb shell
$ adb mkdir -p /system/vendor/overlay
$ exit

有些機台可能本來就有overlay的dir了,像敝司的機台有蠻多客製化的東西,所以overlay的資料夾就已經存在了

接著把apk push進overlay中

$ adb push myOverlays_signed.apk /system/vendor/overlay

push完就重開機吧。

5.啟用overlay

push進去不一定會直接有效,可以下以下指令:

$ adb shell cmd overlay list

會顯示機台中所有overlay的項目,目標以及overlay app的對應列表,以及啟用狀態,像這樣:

可以看到目標(timothy.test.com.myapplication)底下有剛剛我們app的package name(com.test.application.overlay),前面有個[X],表示這個overlay是已經啟用了

如果沒有打叉,那就執行以下command啟用:

$ adb shell cmd overlay enable — user 0 com.test.application.overlay

執行完之後再重開機一次。

6.結果

打開overlay target app,就可以發現該覆蓋的地方已經被覆蓋掉了(背景顏色 & button的字樣)

overlay後的結果

7.結語

當然啦,如果你只是要完成例子中的目的,有更簡單的方式,不需要額外寫一隻app來做overlay。工作上用到的問題,順手memo一下,在系統廠工作總是有一些莫名其妙的需求。

--

--

No responses yet