本篇文章只是整理了一些流行的开源插件化技术,其中言论纯属开源作者,不代表本人观点。
- 完美内置
- 所有插件支持内置于宿主包中
- 高度透明
- 插件编码、布局编写方式与独立应用开发无异
- 插件代码调试与整包开发无异
- 极致剪裁
- 对插件分离所有一切能分离的公共代码、资源
- 无缝链接
- 通过设定URI,宿主、本地化应用插件、本地化web插件、在线网页,以及任何自定义的插件之间能够相互调起与传递参数
- 跨平台
- 目前已支持Android、iOS以及html5插件。并且三者之间可以通过同一套javascript接口进行通信。
各个插件框架功能对比:
DyLA : Dynamic-load-apk @singwhatiwanna, 百度
DiLA : Direct-Load-apk @melbcat
APF : Android-Plugin-Framework @limpoxe
ACDD : ACDD @bunnyblue
DyAPK : DynamicAPK @TediWang, 携程
DPG : DroidPlugin @cmzy, 360
-
功能
\ DyLA DiLA ACDD DyAPK DPG APF Small 加载非独立插件[1] × x √ √ × √ √ 加载.so插件 × × ! [2] × × × √ Activity生命周期 √ √ √ √ × √ √ Service动态注册 × × √ × × √ x [3] 资源分包共享[4] × × ! [5] ! [5] × ! [6] √ 公共插件打包共享[7] × × × × × × √ 支持AppCompat[8] × × × × × × √ 支持本地网页组件 × × × × × × √ 支持联调插件[9] × x × × × × √
-
[1] 独立插件:一个完整的apk包,可以独立运行。比如从你的程序跑起淘宝、QQ,但这加载起来是要闹哪样?
非独立插件:依赖于宿主,宿主是个壳,插件可使用其资源代码并分离之以最小化,这才是业务需要嘛。
-- “所有不能加载非独立插件的插件化框架都是耍流氓”。
-
[2] ACDD加载.so用了Native方法(libdexopt.so),不是Java层,源码似乎未共享。
-
[3] Service更新频度低,可预先注册在宿主的manifest中,如果没有很好的理由说服我,现不支持。
-
[4] 要实现宿主、各个插件资源可互相访问,需要对他们的资源进行分段处理以避免冲突。
-
[5] 这些框架修改aapt源码、重编、覆盖SDK Manager下载的aapt,我只想说“杀(wan)鸡(de)焉(kai)用(xin)牛(jiu)刀(hao)”。
Small使用gradle-small-plugin,在后期修改二进制文件,实现了PP段分区。
-
[6] 使用public-padding对资源id的TT段进行分区,分开了宿主和插件。但是插件之间无法分段。
-
[7] 除了宿主提供一些公共资源与代码外,我们仍需封装一些业务层面的公共库,这些库被其他插件所依赖。
公共插件打包的目的就是可以单独更新公共库插件,并且相关插件不需要动到。
-
[8] AppCompat: Android Studio默认添加的主题包,Google主推的Metrial Design包也依赖于此。大势所趋。
-
[9] 联调插件:使用Android Studio调试宿主时,可直接在插件代码中添加断点调试。
-
透明度
\ ACDD DyAPK APF Small 插件Activity代码无需修改 √ √ √ √ 插件引用外部资源无需修改name × × × √ 插件模块无需修改build.gradle × x × √
Step 1. Clone Small (下载源码)
> cd [你要放Small的目录]
> git clone https://github.com/wequick/Small.git
强烈建议使用git命令行,方便更新维护。Windows用户入口:Git for Windows
后续更新可以使用命令:git pull origin master
Step 2. Import Sample project (导入示例工程)
打开Android Studio,File->New->Import Project... 选择Sample文件夹,导入。
- Sample
示例工程
- app
宿主工程
- app.*
包含Activity/Fragment的组件
- lib.*
公共库组件
- web.*
本地网页组件
- sign
签名文件
- app
顺便说下,这些app.*跟web.*可以从工具栏的按钮单独运行。
其中app.home无法单独运行是因为它只包含一个Fragment,没有Launcher Activity。
Step 3. Build libraries (准备基础库)
> [./]gradlew buildLib -q (-q是安静模式,可以让输出更好看,也可以不加)
Step 4. Build bundles (打包所有组件)
> [./]gradlew buildBundle -q (-q是安静模式,可以让输出更好看,也可以不加)
Step 5. Import DevSample project (导入开发工程)
打开Android Studio,File->New->Import Project... 选择DevSample文件夹,导入。
- DevSample
开发工程
- buildSrc
组件编译插件,用于打包组件
- small
核心库,用于加载组件
- buildSrc
buildSrc在修改后会被自动编译。
其他步骤同上。除了编译单个组件的命令有所不同:
> [./]gradlew :app.main:assembleRelease
P.s. gradlew命令支持缩写,比如
assembleRelease
可以缩写为aR
-
插件是否一定得内置到APK中,如果一定要内置,那这个框架仅仅是为了开发工程中解耦?
内置是为了首次启动更快,如果不内置也行,可以在启动页下载插件。
-
目前开源的插件开发框架有很多,Small 跟这些框架的区别是什么,优缺点呢?
- 百度工程师开发的 Dynamic-load-apk
- melbcat开源的 Direct-Load-apk
- limpoxe开源的 Android-Plugin-Framework
- bunnyblue开源的 ACDD
- 携程工程师开发的 DynamicAPK
- 360工程师开发的 DroidPlugin
- dynamic-load-apk使用代理的方式实现Activity生命周期,插件中不能用this,不够透明;
- Direct-Load-apk对dynamic-load-apk进行了改进,支持this。但也是用代理Activity,实现较为繁琐;
- Android-Plugin-Framework是一个相对完整的框架,但资源分区方案还不够理想,不支持加载.so插件;
- ACDD 使用了osgi,没有细看。。坑点是资源分区要使用修改aapt源码再重新编译的方案;
- DynamicAPK 坑点:修改aapt源码,不支持.so插件;
- DroidPlugin支持对完整apk的动态加载,但是没有关于非独立插件的加载(资源分区要复杂得多);
- 这些框架似乎都不支持AppCompat包(但这很重要,材料设计的Design包等都依赖AppCompat);
- Small的开发其实是跟随1、2、3走过来的。从实际场景出发,基于“轻量、透明、极小化、跨平台”的理念:
- 把核心代码量控制在了一个文件(ApkBundleLauncher)500行以内
- 不修改aapt源码,实现了资源id PP段的再分配(原理见Dynamic load resources)
- 通过对aapt生成的二进制文件的后期加工,最大化分离无用的资源,使得插件包最小达到4k左右
- 支持对本地化网页进行插件打包,实现跨平台
-
目前大多插件框架对Service的支持不太好,Small是否支持,支持度怎么样。
支持动态加载,不支持动态注册。实际场景中,Service的更新频度较低,注册可以预埋在宿主的manifest文件中。
-
插件与宿主必须运行在同一个进程吗? 是否支持插件运行在单独的进程中。
在同一个进程。
-
当插件与宿主共有一个控件时(比方说自己写了一个下拉刷新的控件),开发过程中是不是要插件和宿主里面都含有这个控件? 同样,是否允许插件访问宿主的资源(比如说res)以及数据(SharedPreferences中的数据)。
插件中所有的公共资源、类将会被分离。宿主、各个插件之间的资源可以互相访问,支持SharedPreferences,完全透明。