Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android插件化之資源動態加載

Android插件化之資源動態加載

編輯:關於Android編程

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,這就出錯了。
解決方案有,在插件中如果有添加新的資源,則其命名要安裝字典排序在原有的資源下遞增。當然也有其他方案,自己琢磨吧。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持本站。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved