插件化與組件化的區(qū)別
組件化,也稱模塊化,主要通過拆分單獨(dú)功能模塊及通用模塊來實(shí)現(xiàn)較大應(yīng)用的松耦合。把需要獨(dú)立拆分的業(yè)務(wù)設(shè)計(jì)成一個(gè)模塊,各個(gè)模塊的代碼最終打包成一個(gè)對(duì)應(yīng)的aar,主App和業(yè)務(wù)App設(shè)計(jì)成一個(gè)運(yùn)行殼子,編譯打包時(shí)候使用Gradle做maven依賴即可。
arr是針對(duì)Android Library而言的,是IDE針對(duì)Android Library的打包。本身是zip格式的文件。
插件化:某個(gè)業(yè)務(wù)模塊單獨(dú)做出一個(gè)Apk,主App直接使用插件的方式,如果需要某種功能,那么直接加載某一個(gè)apk,而不是直接依賴代碼的形式。
插件化方案
Fragement加載方案:參考項(xiàng)目AndroidDynamicLoader,云音樂采用類似方案。
Activity代理方案:與Fragment加載原理類似,仍然使用本地代理容器Activity打開頁面,由代理容器對(duì)原Activity生命周期進(jìn)行托管。
Activity占坑方案:采用Hook技術(shù),可動(dòng)態(tài)注冊(cè)和加載Acitivity,可實(shí)現(xiàn)一對(duì)多
此處的Hook是指通過Java反射手段,獲取并修改與系統(tǒng)Server等交互的Internal API來讓框架正常工作的行為。
技術(shù)選型
- 目前成熟且活躍的方案
360的RePlugin、淘寶的atlas、滴滴的VirtualAPK、非商業(yè)組織的Small。
讓模塊最終具備動(dòng)態(tài)性是它們最核心的能力。 - 主要考慮因素
穩(wěn)定(不崩潰);
高可用(具備可插拔/安全校驗(yàn)/內(nèi)外置插件等成熟框架所具備的功能);
可維護(hù)(在前后向兼容時(shí)能提供新老版本對(duì)最新開發(fā)的插件的支持)。參考
RePlugin介紹
介紹
RePlugin是一套適合全面使用的占坑類插件化方案,目標(biāo)是全面插件化。
內(nèi)置插件主要應(yīng)用于基礎(chǔ)重要模塊,外置插件可動(dòng)態(tài)添加,并實(shí)現(xiàn)插件既可獨(dú)立apk存在,也可通過宿主啟動(dòng)。
在實(shí)際中存在小Bug,將外置插件以獨(dú)立apk形式安裝后,宿主第一次啟動(dòng)該插件會(huì)失敗,重啟會(huì)正常啟動(dòng)。
架構(gòu)
系統(tǒng)層——Android:為Android Framework層。只有ClassLoader是Hook的,而AMS、Resources等都沒有做Hook,確保了其穩(wěn)定性。
框架層——RePlugin框架:RePlugin框架層,只有RePlugin是對(duì)“上層完全公開”的,其余均為Internal,或“動(dòng)態(tài)編譯方案”生效后的調(diào)用,對(duì)開發(fā)者而言是“無需關(guān)心”的。
插件層——各類業(yè)務(wù)插件及基礎(chǔ)插件。具體可分為內(nèi)置插件和外置插件。
唯一Hook點(diǎn):ClassLoader
Java中的ClassLoader:
BootClassLoader是系統(tǒng)啟動(dòng)時(shí)創(chuàng)建的。
PathClassLoader是應(yīng)用啟動(dòng)時(shí)創(chuàng)建的,只能加載內(nèi)部dex。
DexClassLoader可以加載外部dex。RePlugin中存在兩個(gè)主要ClassLoaer:
1、RePluginClassLoader: 宿主App中的Loader,繼承PathClassLoader,也是唯一Hook住系統(tǒng)的Loader。
2、PluginDexClassLoader: 加載插件的Loader,繼承DexClassLoader。用來做一些“更高級(jí)”的特性。
四大組件實(shí)現(xiàn)插件化的實(shí)現(xiàn)原理
- Activity 在啟動(dòng)的時(shí)候替換成了合適的占坑的activity , 然后ClassLoader loadClass 的時(shí)候根據(jù)占坑Activity 到真正Activity 的映射關(guān)系,
輸入占坑Activity,返回真正Activity 的類,避免了需要hook. - BroadcastReceiver 是把所有靜態(tài)注冊(cè)都動(dòng)態(tài)注冊(cè)在一個(gè)代理Receiver, 收到廣播在代理Receiver 進(jìn)行分發(fā).
- Service 實(shí)現(xiàn)邏輯是這里其實(shí)是直接在UI線程調(diào)用了service 的相關(guān)生命周期的方法.同時(shí)啟動(dòng)一個(gè)service來提高service所在進(jìn)程優(yōu)先級(jí).
- ContentProvider 這里使用的是代理ccontentprovide,在對(duì)應(yīng)的生命周期使用反射將對(duì)應(yīng)類生成出來. 然后調(diào)用對(duì)應(yīng)的聲明周期方法.
參考
Activity啟動(dòng)的主要流程
- 通過代理類PluginLibraryInternalProxy對(duì)Activity進(jìn)行啟動(dòng)。
- 加載插件,找到目標(biāo)啟動(dòng)的activity的ActivityInfo信息。
- 搜索匹配容器,為要啟動(dòng)的目標(biāo)Activity的分配坑位。將占坑信息與目前Activity建立映射關(guān)系。
分配坑位的優(yōu)先級(jí)是先找上一個(gè)活的->沒有則找空白的->重用沒引用的最老的 - 將Activity信息存入坑位,啟動(dòng)坑位Activity。
- 使用RePluginClassLoader進(jìn)行l(wèi)oadClass
- 根據(jù)坑位找到目標(biāo)activity所在插件,由插件的PluginDexClassLoader去加載activity
- PluginDexClassLoader遵循雙親委派模型流程,仍然沒有找到則從宿主ClassLoader中加載(宿主啟動(dòng)插件activity一般不會(huì)走到這一步)。
動(dòng)態(tài)編譯方案
RePlugig動(dòng)態(tài)編譯方案會(huì)將Activity替換成PluginActivity,使用代理模式進(jìn)行檢查操作確認(rèn)后啟動(dòng)相應(yīng)Activity。具體替換規(guī)則如下:
/* LoaderActivity 替換規(guī)則 */
def private static loaderActivityRules = [
'android.app.Activity' : 'com.qihoo360.replugin.loader.a.PluginActivity',
'android.app.TabActivity' : 'com.qihoo360.replugin.loader.a.PluginTabActivity',
'android.app.ListActivity' : 'com.qihoo360.replugin.loader.a.PluginListActivity',
'android.app.ActivityGroup' : 'com.qihoo360.replugin.loader.a.PluginActivityGroup',
'android.support.v4.app.FragmentActivity' : 'com.qihoo360.replugin.loader.a.PluginFragmentActivity',
'android.support.v7.app.AppCompatActivity': 'com.qihoo360.replugin.loader.a.PluginAppCompatActivity',
'android.preference.PreferenceActivity' : 'com.qihoo360.replugin.loader.a.PluginPreferenceActivity',
'android.app.ExpandableListActivity' : 'com.qihoo360.replugin.loader.a.PluginExpandableListActivity'
]