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