Android的插件化技術,目前已經比較成熟,微信、淘寶、攜程、360手機助手中都應用到了插件化。插件化技術的特色是無需單獨安裝apk,便可運行,即插即用,無需升級宿主應用,減小app的更新頻率,html
除此以外他還能夠下降模塊耦合,按需加載,節省流量等特色。java
表1從是否支持四大組件、是否須在主manifest預註冊等多個維度對主流開源框架進行對比,從而篩選出比較符合項目的框架有VirtualAPK、RePlugin。android
表1主流開源框架的對比git
特性github |
DynamicLoadApk微信 |
DynamicAPK 架構 |
Smallapp |
DroidPlugin框架 |
VirtualAPKless |
RePlugin |
ACDD/Atlas |
支持四大組件 |
只支持Activity |
只支持Activity |
只支持Activity |
全支持 |
全支持 |
全支持 |
全支持 |
組件無需在宿主manifest中預註冊 |
✓ |
× |
✓ |
✓ |
✓ |
✓ |
× |
插件能夠依賴宿主 |
✓ |
✓ |
✓ |
× |
✓ |
輕度依賴 |
✓ |
支持PendingIntent |
× |
× |
× |
✓ |
✓ |
✓ |
未明確 |
Android特性支持 |
大部分 |
大部分 |
大部分 |
幾乎所有 |
幾乎所有 |
幾乎所有 |
未明確 |
兼容性適配 |
通常 |
通常 |
中等 |
高 |
高 |
高 |
未明確 |
插件構建 |
無 |
部署aapt |
Gradle插件 |
無 |
Gradle插件
|
Gradle插件 |
部署aapt |
框架輕重 |
|
|
|
相對輕量 |
相對輕量 |
相對輕量 |
重量 |
支持安卓版本 |
|
|
|
|
API Level 15+ |
API Level 9+ |
|
接入難度 |
|
|
|
|
中 |
易 |
難 |
側重階段 |
|
|
|
運行期 |
運行期 |
運行期 |
編譯期 |
熱修復能力 |
無 |
無 |
無 |
無 |
無 |
無 |
有 |
插件安裝後能否刪除 |
|
|
|
|
不能 |
不能 |
能夠 |
插件更新方式 |
|
|
|
|
插件獨立更新 |
插件獨立更新 |
插件及宿主須同時更新 |
對錶1各插件化框架進行篩選,最後可得出VirtualAPK爲最優候選框架。
如下對重點對VirtualAPK作詳細調研。
3.1 VirtualAPK主要優點
功能完備: 支持幾乎全部的Android特性, 四大組件均不須要在宿主manifest中預註冊,每一個組件都有完整的生命週期;
優秀的兼容性: 兼容市面上幾乎全部的Android手機,資源方面適配小米、Vivo、Nubia等,對未知機型採用自適應適配方案, 極少的Binder Hook且hook過程作了充分的兼容性適配,插件運行邏輯和宿主隔離,確保框架的任何問題都不會影響宿主的正常運行。
入侵性極低: 四大組件無需繼承特定的基類,插件能夠依賴宿主中的代碼和資源,經過Gradle插件來完成插件的構建,整個過程對開發者透明。
以下是VirtualAPK的總體架構圖。
詳情參見:
https://github.com/didi/VirtualAPK/wiki
3.2 VirtualAPK主要工做原理
VirtualAPK 對於插件沒有額外的約束,原生的 apk 便可做爲一個插件。插件工程編譯生成 apk 後,經過宿主 App 加載,每一個插件 apk 被加載後,都會在宿主中建立一個單獨的 LoadedPlugin
對象。如上圖所示,經過這些 LoadedPlugin
對象,VirtualAPK 就能夠管理插件並賦予插件新的意義,使其能夠像手機中安裝過的App同樣運行。
圖2 插件開發及加載過程
圖3描述了LoadedPlugin的結構,LoadedPlugin包含有插件apk中定義的activities、services、
broadcast receivers、providers、classloader、resources等資源,LoadedPlugin實例化及加載時機如圖2所示,由PluginManager執行其loadPlugin方法將插件apk加載進dalvik。
圖3 LoadedPlugin結構
3.2.1基本原理
動態代理
基於JDK Proxy、InvocationHandler實現對任意java類的AOP控制。
Hook AMS\Instrumentation\IContentProvider
Hook技術基於反射和動態代理技術。
VirtualAPK經過Hook技術,從而實現對Activity、Service、ContentProvider的控制。
Hook AMS
Hook要點爲基於動態代理技術實現ActivityManagerProxy,以實現攔截ActivityManagerService的方法調用,而後利用反射將ActivityManagerNative實例替換掉ActivityManagerNative的gDefault成員。
圖4 AMS Hook代碼
Hook Instrumentation
Hook要點爲擴展Instrumentation以實現對Activity啓動過程的控制,而後使用反射將擴展
Instrumentation實例替換ActivityThread對象原來的Instrumentation對象。
圖5 Instrumentation Hook代碼
Hook IContentProvider
圖6 IContentProvider Hook代碼
合併宿主和插件的ClassLoader
dalvik.system.BaseDexClassLoader內部定義了DexPathList,用於記錄去哪裏加載指定的類,而DexPathList內部定義了dexElements,專門記錄已加載的dex。
對於宿主工程來講,在加載插件工程後,將插件dex插入到宿主工程的dexElements集合後面便可,在以後的運行中就能夠按需加載插件中的class。
須要注意的是,插件中的類不能夠和宿主重複 。
圖 7宿主和插件Dex合併示意圖
合併插件和宿主的資源 重設插件資源的packageId,將插件資源和宿主資源合併
去除插件包對宿主的引用 構建時經過Gradle插件去除插件對宿主的代碼以及資源的引用
3.2.2四大組件的實現原理
Activity
採用宿主manifest中佔坑的方式來繞過系統校驗,而後再加載真正的activity;
圖8描述了VirtualAPK根據launchMode定義了2(standard) + 8(singleTop) + 8(singleTask) + 8(singleInstance) = 26個SubActivity坑.
圖 8 CoreLibrary工程的AndoidManifest
圖9描述了啓動插件apk中activity的主要過程,也是VirtualAPK插件無需在AndroidManifest預註冊的原理。該原理是VirtualAPK預先在AndroidManifest註冊一些StubActivity,這些StubActivity並無實際的代碼實現,在以後啓動插件Activity時,VirtualAPK將插件Activity更名爲StubActivity,在繞過Android對啓動插件Activity的合法校驗後,再將StubActivity改回原來的Activity名稱。
該過程有兩個關鍵點:
1) 將插件Activity轉換爲StubActivity;
該階段的做用是繞過Android對啓動Activity的限制:Activity必須先在AndroidManifest註冊,不然不能被startActivity。具體實現爲利用hook的VAInstrumentation攔截Instrumentation的execStartActivity方法,並在該方法內將待啓動的插件Activity名稱改 爲StubActivity。
2) 將StubActivity還原爲插件Activity。
此階段的做用是繼1)階段繞過系統對插件Activity的合法校驗後,將StubActivity名稱改回原來的名稱,以使得在Instrumentation.newActivity時實例並啓動正確的目標Activity。
圖9插件Activity啓動過程
Service 動態代理AMS,攔截service相關的請求,將其中轉給Service Runtime
去處理,Service Runtime
會接管系統的全部操做;
插件Service的啓動/關閉基於被Hook的AMS,VirtualAPK在攔截到start/bind/stop/unbind Service的調用後,會執行ActivityManagerProxy的invoke方法,以後控制流程和數據會轉交給ServiceDelegate對象,以後ServiceDeletegate會判斷待啓動的Service是本地服務仍是遠程服務,若是是本地服務,VirtualAPK會反射調用Service相應生命週期的方法;若是是遠程服務,VirtualAPK先將目標插件apk加載進來,而後再反射調用目標Service的生命週期方法。
圖10插件Service啓動過程
Receiver 將插件中靜態註冊的receiver從新註冊一遍;
ContentProvider 動態代理IContentProvider,攔截provider相關的請求,將其中轉給Provider Runtime
去處理,Provider Runtime
會接管系統的全部操做。
4.1 VirtualApk是否支持混淆
結論:VirtualApk主工程支持混淆,子工程也支持混淆
4.2、調研如下問題:
4.2.1 插件下載後,需不須要重啓app才能完成加載?
結論:插件下載後不須要App重啓便可生效,但插件升級後需重啓app.
1) VirtualApk
github項目描述原話:
VirtualApk can dynamically load and run an APK file(we call it LoadedPlugin) seamless as an installed application.
說明插件下載後不需重啓app便可生效運行;
2) 反證法思路:VirtualApk是當下最流行的插件化框架,若是其在插件下載後須要app重啓,那麼該框架確定不會被業界承認;
3) 實踐證明插件下載後不需app重啓便可生效,但插件更新後需app重啓方可生效。
嘗試PluginManager卸載再裝載組件的思路,實驗結果未能成功自動更新升級後的組件。
4.3插件中和主app若是包含一樣的庫,執行中會不會遇到問題?
結論:所查閱文獻明確說明 插件不能包含有主工程的代碼,但插件工程在編譯時應該可使用主工程代碼協助編譯,只是編譯完成後就不能有主工程代碼。
經實際編碼驗證,若是插件和主app包含一樣的代碼,以主app代碼爲準。
參考文獻:
https://blog.csdn.net/u012439416/article/details/76595643
https://juejin.im/entry/59e8618cf265da43143fcf68
4.4不用反射的方法,能不能讓宿主直接調用插件中的類和方法?
結論:Java類經ClassLoader加載到JVM後,有反射和接口調用兩種方式
Class A = Class.forName("com.some.class.A");
//反射方式
Method method = A.getDeclaredMethod(...);
method.invoke(null, ...);
//接口方式
InterfaceA objA = (InterfaceA) A.newInstance(...);
objA.interfaceMethod();
InterfaceA定義:
interface InterfaceA {
void interfaceMethod();
}
5.1 宿主工程直接調用插件工程的四大組件 宿主工程調用插件工程的Activity/Service, 插件工程內部再調用本身或其餘插件的四大組件 表2 VirtualAPK實驗結果
方式 |
Activity |
Service |
BroadcastReceiver |
ContentProvider |
宿主工程直接調用插件工程 |
✓ |
× |
✓ |
✓ |
宿主工程間接調用插件工程 |
✓ |
× |
✓ |
✓ |
5.2 反射並使用插件工程的類 ✓ 在插件工程定義一工具類,而後在宿主工程Class.forName加載並使用該工具類,最後編譯並打包運行驗證效果,經實際驗證該feature功能正常。 5.3 混淆驗證 ✓ 驗證插件工程對宿主工程有單向依賴的情形。
表3 VirtualAPK實驗結果
序號 |
宿主工程是否混淆 |
插件工程是否混淆 |
運行效果 |
1 |
混淆 |
混淆 |
✓ |
2 |
混淆 |
未混淆 |
× |
3 |
未混淆 |
混淆 |
✓ |
4 |
未混淆 |
未混淆 |
✓ |
根據是否支持四大組件、是否需在宿主工程manifest預註冊、插件工程是否可依賴宿主工程等指標,對錶1各插件化框架進行篩選,最後可得出VirtualAPK爲最優候選框架。但通過實際編碼驗證,四大組件除Service外均可被VirtualAPK支持。
Android 360開源全面插件化框架RePlugin實戰
https://blog.csdn.net/qiyei2009/article/details/78236520 工程github地址:https://github.com/Qihoo360/RePlugin
ACDD使用教程
http://www.jksoftcn.com/acddshi-yong-jiao-cheng.html
Android插件化框架
https://www.cnblogs.com/yoyohong/p/7599981.html
OpenAtlas之4四 資源分佈結構解析
http://www.lxway.com/28562.htm
Android OpenAtlas初識
https://www.aliyun.com/jiaocheng/90603.html
Android插件化開發之OpenAtlas初體驗
https://www.aliyun.com/jiaocheng/47101.html?spm=5176.100033.2.10.LuYTOz
Android插件化開發之OpenAtlas中四大組件與Application功能的驗證https://blog.csdn.net/sbsujjbcy/article/details/47952269
Atlas、VirtualAPK、RePlugin三者的體驗感覺
https://www.jianshu.com/p/ceded2da7847
Android插件化:從入門到放棄
http://www.infoq.com/cn/articles/android-plug-ins-from-entry-to-give-up