Android 插件框架实现思路及原理
插件框架實(shí)現(xiàn)思路及原理
一、技術(shù)可行性
a)?apk的安裝處理流程
i.?apk會(huì)copy到/data/app;
ii.?解壓apk中的class.dex,并對(duì)其進(jìn)行優(yōu)化,獲得odex(即JIT)。最后保存到/data/dalvik_cache;
iii.?還有一些權(quán)限和包信息,會(huì)緩存到/data/system中的packages.list和packages.xml中。
b)?在android上,對(duì)apk包的加載邏輯
i.?加載邏輯
?Zygote(孵化器)在成功啟動(dòng)一Android進(jìn)程后,會(huì)根據(jù)packages.list的內(nèi)容(啟動(dòng)時(shí)會(huì)加載到system_process中的pakcagemanager中),把odex文件,加載到dalvik中,完成邏輯的加載;
ii.?資源讀取
資源讀取,主要有兩三個(gè)類,分別是Resource、AssertManager和LayoutInflater。
當(dāng)在顯示界面時(shí),就通過(guò)這三個(gè)類讀取資源。
c)?結(jié)論和猜想
i.?apk相對(duì)于整個(gè)android系統(tǒng)而言,其本身就是一種插件形式體現(xiàn)。根據(jù)上面關(guān)于邏輯和資源的讀取概述,完全是可以靜默實(shí)現(xiàn)的。其次,class.dex并沒(méi)有包含Android?SDK的代碼,只是保留對(duì)Android?SDK接口的調(diào)用。?可以這樣想象,Android?SDK即插件框架,而Android?OS即為整個(gè)插件的宿主環(huán)境。因此這就可以解釋了,為什么在1.x編譯的代碼,在2.x甚至3.x都可以運(yùn)行,因?yàn)橹灰寮拗鞯慕涌?/span>(即Android?SDK)不變,插件運(yùn)行時(shí)所調(diào)用的接口都可以被找到。
ii.?為了減少內(nèi)存占用,Resource、AssertManager和LayoutInflater必然不會(huì)把apk中的所有資源都加載進(jìn)來(lái),而是用時(shí)才加載并緩存,而且還有一些的處理機(jī)制(如最不常用清除等)。因此這些類當(dāng)中,必然存在一個(gè)指明資源路徑的字段或者結(jié)構(gòu)。
iii.?要保證兼容性,插件框架公開(kāi)給插件的接口,必須遵守Open-Close(開(kāi)發(fā)-封閉)原則。另外,一些已經(jīng)廢棄掉接口,同樣需要保留。比如Service中的setForeground和JDK的中關(guān)于Thread的一些接口等。
iv.?可以嘗試通過(guò)反射,修改Resource、AssertManager和LayoutInflater中指明資源路徑的字段;另外,還可以查看源碼,查找設(shè)置資源路徑的方法。
二、技術(shù)實(shí)現(xiàn)要點(diǎn)
a)?邏輯加載
i.?針對(duì)接口編程。這個(gè)是所有插件框架的基本設(shè)計(jì)模型。
ii.?通過(guò)DexClassLoader加載插件所實(shí)現(xiàn)的插件接口,詳細(xì)可參考PluginManagerImpl中的parserPlugin方法實(shí)現(xiàn),關(guān)鍵代碼如下:
b)?AssertManager的實(shí)現(xiàn)
經(jīng)查閱Android源碼,發(fā)現(xiàn)AssertManager的實(shí)例生成,用到兩個(gè)隱藏的方法,如下所示:
通過(guò)以上代碼,我們就可以得到我們插件的AssertManager了,關(guān)鍵代碼如下所示:
c)?Resource的實(shí)現(xiàn)
有了插件專用的AssertManager,那么插件的Resource也輕易得到了,關(guān)鍵代碼如下所示:
d)?LayoutInflater的實(shí)現(xiàn)
一般我們要獲取LayoutInflater,都必須通過(guò)Context來(lái)獲得,即是說(shuō)LayoutInflater的資源讀取,都是通過(guò)Context的getResoure以及getAssert讀取,是直接跟宿主掛勾的。這里有兩個(gè)方法可選選擇:
其一,自己重寫(xiě)Context類,并把getResoure和getAssert的返回值改為上面所得的插件資源相關(guān)的實(shí)例,即包裝法;
其二,考慮到平時(shí)我們用LayoutInflater時(shí),主要是用來(lái)加載布局文件(XML),因此可以投機(jī)取巧點(diǎn),只針對(duì)inflater進(jìn)行修改。
目前框架采用的是第二種方法。先看看LayoutInflater的源碼實(shí)現(xiàn),如下所示:
因此,只調(diào)用插件的Resources,并調(diào)用其第二個(gè)方法,并可實(shí)現(xiàn)加載插件的布局問(wèn)題,為了方便使用,定義了一個(gè)ILayoutInflater接口,封裝實(shí)現(xiàn)細(xì)節(jié),關(guān)鍵代碼如下:
事實(shí)上,通過(guò)以上的方法,還是無(wú)法完成插件XML布局文件的加載,通過(guò)跟蹤源碼,會(huì)發(fā)生View的生成,還需要因?yàn)槔玫疆?dāng)前Context(或Activity)的一個(gè)類型Theme的實(shí)例。跟蹤過(guò)程如下:
l?View(Context?context,?AttributeSet?arrts,?int?defStyle)
l?Context.obtainStyledAttributes(AttrobiteSet?arrts,?int[]??attrs,?int?defStyleAttr,?int?defStyleRes)
l?Context.getTheme()
l?......
而這個(gè)Theme類型,是Resource的一個(gè)內(nèi)部類,不單可以直接引用Resource的,還通過(guò)Context保存AssertManager的引用。源碼如下:
因此,我們還需要通過(guò)反射的方式,把當(dāng)前Theme的實(shí)例,替換成我們插件的。當(dāng)XML布局文件解釋成功后,再恢復(fù)過(guò)來(lái)。留意上面代碼中的begin和end方法,就是這個(gè)過(guò)程的封裝。關(guān)鍵代碼如下:
而插件的Theme實(shí)例,可通過(guò)Resource的newTheme獲得,關(guān)鍵代碼如下:
e)?混合資源解析的實(shí)現(xiàn)
基本上,上面的有了上面的三個(gè)類,就可以完全加載插件的讀取插件的資源和邏輯。但往往事情并不是這么簡(jiǎn)單。
考慮到UI的可重用性,插件里,往往會(huì)很多的用到宿主所提示的UI庫(kù)接口。因此,就需要考慮,當(dāng)在解釋插件的xml布局時(shí),如何混合使用兩方的資源。從上面的過(guò)程可得,無(wú)論是Resources、AssertManager還是LayoutInflater,都是同時(shí)只能針對(duì)一方資源。當(dāng)在插件的布局文件中,使用了兩方的文件,必然會(huì)因?yàn)檎也坏劫Y源,解釋出錯(cuò)。
不過(guò),LayoutInflater,提示了一個(gè)setFactory,可以在解釋XML布局文件時(shí),優(yōu)先解釋。關(guān)鍵代碼如下:
三、待完善的地方
l?在宿主中定義的資源,目前除了自定義view之外,都不可以使用。比如文字、顏色值、樣式等。
l?插件的權(quán)限,必須是宿主的子集。
l?插件中包含so的加載邏輯,還沒(méi)有實(shí)現(xiàn)。
l?目前插件的加載,都是加載dex,而不是odex。還需要查閱源碼,手工實(shí)現(xiàn)這個(gè)優(yōu)化過(guò)程。
原文地址: http://blog.csdn.net/l173864930/article/details/12235375
總結(jié)
以上是生活随笔為你收集整理的Android 插件框架实现思路及原理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Android框架攻击之Fragment
- 下一篇: Android 热修复 HotFix 混