Android外掛程式化之資源動態載入_Android

來源:互聯網
上載者:User

Android外掛程式化之資源動態載入

一.概述

Android外掛程式化的一個重要問題就是外掛程式資源訪問問題,先列出會面對的問題

1.如何載入外掛程式資源
2.如何處理外掛程式資源與宿主資源的處突:外掛程式化資源問題要做到的效果是,如果我們要擷取的資源在外掛程式中找得到,則載入優先載入外掛程式的,如果找不到,則到宿主資源中找。這樣能做到動態更新的效果。
3.如何確保外掛程式和宿主使用到的是被修改過的資源。

二.原理分析

在做一件事之前必須先弄清楚原理,所以,這裡先要弄清楚Android的資源體系原理。

1.資源鏈

 

Context:一個apk裡面其context的個數為application+Activity+service的總和,因為他們都是繼承context的,然而context只是一個抽象類別,其真正的實作類別是ContextImpl,那。拿Activity來說,在Activity的啟動流程中,會在ActivityThread的performLaunchActivity()方法中調用Activity的attach方法把ContextImp執行個體傳給Activity(即賦值給Activity內的成員變數mBase)。


Resources:ContextImpl內有一個Resources的成員變數mResources,代表的是應用的資源,我們平時在調用getResources()方法擷取到的是該Resources。

AssetManager:Resources內部的一個重要成員是AssetManager(mAssets),其指向的是apk的資源路徑,資源的擷取最終都是通過它來得到的。這裡需要注意的是AssetManager並不是Resources獨立持有的,也就是說系統在擷取資源的時候不一定是通過Resources擷取的,有時候是直接通過AssetManager來擷取,比如TypedArray,之前就踩過這個坑。 

2.Android是如何構造一個應用的資源的,並且是如何傳遞給我們使用的,這個要講的東西非常的多,可以看另一篇文章,這裡主要講資源外掛程式化。

三.問題的解決方案

1.載入外掛程式資源

資源的載入最後是通過AssetManager內的一個方法addAssetPath(String path)


該方法接收的參數是外掛程式apk的路徑,內部會調用native方法把外掛程式apk對應的資源載入進來。然而該方法是hide的,我們不能直接調用,所有只能通過反射。

 

這樣就成功構造出一個指向外掛程式資源的AssetManager。當然這時候還不能使用,還要調用AssetManager的ensureStringBlocks()方法來初始化其內部參數,同樣得使用反射。


2.如何解決外掛程式資源與宿主資源的處突

如果使用到的資源,外掛程式和宿主都同時存在,則使用外掛程式的資源;如果使用到的資源只有外掛程式有,則使用外掛程式的;如果使用到的資源只有宿主有的,則使用宿主的。

AssetManager的addAssetPath()方法調用native層AssetManager對象的addAssetPath()方法,通過查看c++代碼可以知道,該方法可以被調用多次,每次調用都會把對應資源添加起來,而後來添加的在使用資源是會被首先搜尋到。可以怎麼理解,C++層的AssetManager有一個存放資源的棧,每次調用addAssetPath()方法都會把資來源物件壓如棧,而在讀取搜尋資源時是從棧頂開始搜尋,找不到就往下查。所以我們可以這樣來處理AssetManager並得到Resources

 

其中dexPath2為宿主apk路徑,dexPath為外掛程式apk路徑,superRes為宿主資源,resources為融合外掛程式與宿主的資源。

3. 如何確保外掛程式和宿主使用到的是被修改過的資源:
這是很重要的一步,之前我們已經成功擷取資源並對其進行修飾,現在要做的是用它替換掉Android為我們產生的那個資源,這就是hook的思想。

使用到資源的地方歸納起來有兩處,一處是在Java代碼中通過Context.getResources擷取,一處是在xml檔案(如布局檔案)裡指定資源,其實xml檔案裡最終也是通過Context來擷取資源的只不過是他一般擷取的是Resources裡的AssetManager。所以,我們可以在Context對象被建立後且還未使用時把它裡面的Resources(mResources)替換掉。之前說過,整個應用的Context數目等於Application+Activity+Service的數目,Context會在這幾個類建立對象的時候建立並添加進去。而這些行為都是在ActivityTHread和Instrumentation裡做的。

以Activity為例,步驟如下:

a: Activity對象的建立是在ActivityThread裡調用Instrumentation的newActivity方法

ActivityThread:

 

Instrumentation:

 

b: Context對象的建立是在ActivityThread裡調用createBaseContextForActivity方法
ActivityThread: 


c: Activity綁定Context是在ActivityThread裡調用Activity對象的attach方法,其中appContext就是上面建立的Context對象
ActivityThread:

 

d: Activity的onCreate()方法的回調是在ActivityThread裡調用Instrumentation的callActivityOnCreate()方法
ActivityThread:

 

替換掉Activity裡Context裡的Resources最好要早,基於上面的觀察,我們可以在調用Instrumentation的callActivityOnCreate()方法時把Resources替換掉。那麼問題又來了,我們如何控制callActivityOnCreate()方法的執行,這裡又得使用hook的思想了,即把ActivityThread裡面的Instrumentation對象(mInstrumentation)給替換掉,同樣得使用反射。步驟如下

a: 擷取ActivityThread對象

ActivityThread裡面有一個靜態方法,該方法返回的是ActivityThread對象本身,所以我們可以調用該方法來擷取ActivityTHread對象

 

然而ActivityThread是被hide的,所以得通過反射來處理,處理如下: 


b: 擷取ActivityThread裡的Instrumentation對象 


c: 構建我們自己的Instrumentation對象,並從寫callActivityOnCreate方法
在callActivityOnCreate方法裡要先擷取當前Activity對象裡的Context(mBase),再擷取Context對象裡的Resources(mResources)變數,在把mResources變數指向我們構造的Resources對象,做到移花接木。

 

MyInstrumentation:

 

d: 最後,使ActivityThread裡面的mInstrumentation變數指向我們構建的MyInstrumentation對象。 


代碼 


四.應用
資源動態載入的一個應用當然就是Android外掛程式化方面的使用。還有一個應用就是換膚功能,只需要在在工程裡添加這些代碼(當然還要處理一些邏輯),然後使用者想要給應用換皮膚,主題等,即可從後台下載外掛程式apk,放在指定檔案夾就可以關係應用的資源,起到換膚的效果。當然,資源動態載入還有其他應用方法,自己琢磨咯!!!

五.存在問題

1.相容性問題,因為hook要使用反射,從而來擷取系統hide或類的私人屬性。把它們隱藏是因為它們的不穩定性,如果哪天Google覺得那個變數的名稱起的不吉利給改了,那就報錯了。當然解決方案還是有的,就是為不同的API寫不同的代碼。

2.R方面的問題。當我們添加了一個資源(如在String.xml裡添加了一個String),則系統會為我們在R裡面為該資源產生一個int型的id與之對應,使用的時候是根據該id找到對應的資源。資源id是按照資源名稱的字典順序來遞增的。拿String來說。
假如我們的String.xml裡聲明了名稱為za,zb的資源

 

則會在R裡面產生相應的id 


基於上面的觀察,我們會發現一個問題:舉個例子
宿主資源情況為:存在za(id=0x7f060004)  zb(id=0x7f060005)
外掛程式資源情況為:存在za(id=0x7f060004)  zab(id=0x7f060005)   ab(0x7f060006)

這時候在宿主裡擷取資源zb,則根據上面所說,會根據id=0x7f060005先存在外掛程式資源,這時候得到的是zab而不是zb,這就出錯了。
解決方案有,在外掛程式中如果有添加新的資源,則其命名要安裝字典排序在原有的資源下遞增。當然也有其他方案,自己琢磨吧。

以上就是本文的全部內容,希望對大家的學習有所協助,也希望大家多多支援雲棲社區。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.