1啸如、插件框架有兩個作用:一是“自解耦”迹蛤,二是“免安裝”样刷。
目標:“像Web一樣開發(fā)App”則是一個我們后期達成的目標蒜撮,這大概是“自解耦”和“免安裝”的組合形式
1.自解耦指的是:一個應用原本由一份代碼編譯而成暴构,希望改成將其中的一些功能單獨編譯,像插件一樣動態(tài)插在主應用上段磨。這樣一來可是使主應用體積變小取逾,下載安裝更方便。二來可以是比較獨立的功能可以單獨開發(fā)調試苹支,甚至單獨更新版本砾隅。
2.免安裝指的是:一個應用原本需要安裝過程才能啟動運行,希望改為無需安裝即可從一個已經安裝運行的App中啟動起來债蜜。這一需求的主要目的是提高流量復用的能力晴埂。
還未具備的能力:1、下載一部分啟動一部分的能力寻定。2儒洛、ContentProvider
2、宿主和插件使用同一個包名的好處
在執(zhí)行插件代碼的過程中狼速,系統(tǒng)可能會調用一些(公有的或私有的)接口獲取應用的applicationId琅锻,
然而插件從真正意義上來說并沒有安裝到設備上,如果插件的applicationId和宿主的applicationId不相同,系統(tǒng)獲取到插件的applicationId是一個沒有安裝過的包名恼蓬,系統(tǒng)就因此crash惊完。
為了避免出現上述情況,有兩種方法:
- hook系統(tǒng)接口处硬,需要兼容各種OEM系統(tǒng)以及Android各版本
- 插件的applicationId和宿主的applicationId保持一致
3小槐、Fragment調試很麻煩:
比如業(yè)務插件里有一個com.xx.GiftFragment類,實際運行時這個類的名字就變成了com.xx.GiftFragment_郁油。這就導致在com.xx.GiftFragment的源碼上打斷點是斷不下來的本股。必須在程序運行起來之后,用IDE的重命名功能把它改名為com.xx.GiftFragment_桐腌,使得源碼和運行時類名字一致才能斷點。
4苟径、缺點:
Shadow開源的代碼目前沒有包括插件下載和版本檢查實現的案站。
加固
卸載插件
ContentProvider
Fragment調試很麻煩
插件A同插件B有沖突so
插件化要解決的關鍵問題是:插件Activity能收到生命周期回調
舊框架使用反射的地方:attach初始化activity,super.onCreate()獲取私有變量傳遞給我們自己的new的activity
shdow的關鍵點:使用AOP和字節(jié)碼編輯將插件activity繼承自activity的類替換為插件Activity繼承shadowActivity
Activity和Service最大的區(qū)別是Activity是有狀態(tài)的棘街,Service是無狀態(tài)的蟆盐。
對于插件框架來說,有兩點十分必要遭殉。
一是插件一般都是熱更新的石挂,質量上要求可能會降低一些,一旦出現Crash不會影響其他進程的組件险污。
二是Android的JVM虛擬機不支持Native動態(tài)庫反加載痹愚,所以在同一個進程中相同so庫的不同版本即不能同時加載,也不能換著加載蛔糯,會造成插件和宿主存在so庫沖突拯腮。
動態(tài)化原理:
C語言需要編譯和連接兩個步驟,java則編譯成字節(jié)碼蚁飒,在運行時才會去查找用到的類动壤,在用到的時候再去替換對應真正要啟動的類
container代理殼子:
我們可以通過修改ClassLoader的parent,為ClassLoader新增一個parent淮逻。將原本的PathClassLoader->BootCalssLoader結構變?yōu)镻athClassLoader ——> DexClassLoader ——> BootCalssLoader
Container的動態(tài)化是使用了唯一一次反射修改私有變量琼懊。ClassLoader的parent域不屬于非公開API,甚至不是Android的代碼爬早,而是JDK的代碼
view同名問題:
插件和宿主存在同名的view會報錯哼丈,因為LayoutInflater在inflated的時候做了一層緩存,以View的類名作為Key保存了Class對象凸椿。
一個自定義的Factory削祈,然后復制了原本內置構造邏輯的代碼。在這段邏輯中,也有緩存機制髓抑。但是我們將緩存的Key添加了標記插件apk的“partKey”作為一部分咙崎,這樣相同名字的View在緩存中就是不同的Key了
webview加載插件資源的問題:
替換ShadowWebView,攔截請求吨拍,將file:///android_asset/協(xié)議都修改成http://android.asset/協(xié)議
so加載的問題
插件A同插件B有沖突so褪猛,又需要在同一個進程中工作,也需要so的設計方自行解決羹饰。
Android系統(tǒng)不允許在一個進程中混用64位的so和32位的so伊滋,目前的解決方案是讓宿主先加載一個32位的so