Android Plugin 插件化技术-Small插件框架

本篇文章只是整理了一些流行的开源插件化技术,其中言论纯属开源作者,不代表本人观点。

一、Small 简介:做最轻巧的跨平台插件化框架

  • 完美内置
    • 所有插件支持内置于宿主包中
  • 高度透明
    • 插件编码、布局编写方式与独立应用开发无异
    • 插件代码调试与整包开发无异
  • 极致剪裁
    • 对插件分离所有一切能分离的公共代码、资源
  • 无缝链接
    • 通过设定URI,宿主、本地化应用插件、本地化web插件、在线网页,以及任何自定义的插件之间能够相互调起与传递参数
  • 跨平台
    • 目前已支持Android、iOS以及html5插件。并且三者之间可以通过同一套javascript接口进行通信。
二、Small Android(支持平台:Android API 15(4.0.3))

各个插件框架功能对比:

  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动态注册 × × × × [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文件夹,导入。

Small sample

  • Sample 示例工程
    • app 宿主工程
    • app.* 包含Activity/Fragment的组件
    • lib.* 公共库组件
    • web.* 本地网页组件
    • sign 签名文件

顺便说下,这些app.*跟web.*可以从工具栏的▶️按钮单独运行。
其中app.home无法单独运行是因为它只包含一个Fragment,没有Launcher Activity。

Step 3. Build libraries (准备基础库)

> [./]gradlew buildLib -q (-q是安静模式,可以让输出更好看,也可以不加)

Build libraries

Step 4. Build bundles (打包所有组件)

> [./]gradlew buildBundle -q (-q是安静模式,可以让输出更好看,也可以不加)

Build bundles

步骤3跟4,如果你喜欢,也可以在Gradle任务导航里运行
Small tasks

单独编译一个组件可以使用 [./]gradlew -p web.about assembleRelease
或者
Sub tasks

Step 5. Import DevSample project (导入开发工程)

打开Android Studio,File->New->Import Project... 选择DevSample文件夹,导入。

Small devsample

  • DevSample 开发工程
    • buildSrc 组件编译插件,用于打包组件
    • small 核心库,用于加载组件

buildSrc在修改后会被自动编译。

其他步骤同上。除了编译单个组件的命令有所不同:

> [./]gradlew :app.main:assembleRelease

P.s. gradlew命令支持缩写,比如assembleRelease可以缩写为aR


四、实现Android插件化的核心技术:
  1. 插件是否一定得内置到APK中,如果一定要内置,那这个框架仅仅是为了开发工程中解耦?

    内置是为了首次启动更快,如果不内置也行,可以在启动页下载插件。

  2. 目前开源的插件开发框架有很多,Small 跟这些框架的区别是什么,优缺点呢?

    1. 百度工程师开发的 Dynamic-load-apk
    2. melbcat开源的 Direct-Load-apk
    3. limpoxe开源的 Android-Plugin-Framework
    4. bunnyblue开源的 ACDD
    5. 携程工程师开发的 DynamicAPK
    6. 360工程师开发的 DroidPlugin
    1. dynamic-load-apk使用代理的方式实现Activity生命周期,插件中不能用this,不够透明;
    2. Direct-Load-apk对dynamic-load-apk进行了改进,支持this。但也是用代理Activity,实现较为繁琐;
    3. Android-Plugin-Framework是一个相对完整的框架,但资源分区方案还不够理想,不支持加载.so插件;
    4. ACDD 使用了osgi,没有细看。。坑点是资源分区要使用修改aapt源码再重新编译的方案;
    5. DynamicAPK 坑点:修改aapt源码,不支持.so插件;
    6. DroidPlugin支持对完整apk的动态加载,但是没有关于非独立插件的加载(资源分区要复杂得多);
    7. 这些框架似乎都不支持AppCompat包(但这很重要,材料设计的Design包等都依赖AppCompat);
    8. Small的开发其实是跟随1、2、3走过来的。从实际场景出发,基于“轻量、透明、极小化、跨平台”的理念:
      • 把核心代码量控制在了一个文件(ApkBundleLauncher)500行以内
      • 不修改aapt源码,实现了资源id PP段的再分配(原理见Dynamic load resources
      • 通过对aapt生成的二进制文件的后期加工,最大化分离无用的资源,使得插件包最小达到4k左右
      • 支持对本地化网页进行插件打包,实现跨平台
  3. 目前大多插件框架对Service的支持不太好,Small是否支持,支持度怎么样。

    支持动态加载,不支持动态注册。实际场景中,Service的更新频度较低,注册可以预埋在宿主的manifest文件中。

  4. 插件与宿主必须运行在同一个进程吗? 是否支持插件运行在单独的进程中。

    在同一个进程。

  5. 当插件与宿主共有一个控件时(比方说自己写了一个下拉刷新的控件),开发过程中是不是要插件和宿主里面都含有这个控件? 同样,是否允许插件访问宿主的资源(比如说res)以及数据(SharedPreferences中的数据)。

    插件中所有的公共资源、类将会被分离。宿主、各个插件之间的资源可以互相访问,支持SharedPreferences,完全透明。


本篇文章只是整理了一些流行的开源插件化技术,其中言论纯属开源作者,不代表本人观点。

一、Small 简介:做最轻巧的跨平台插件化框架

  • 完美内置
    • 所有插件支持内置于宿主包中
  • 高度透明
    • 插件编码、布局编写方式与独立应用开发无异
    • 插件代码调试与整包开发无异
  • 极致剪裁
    • 对插件分离所有一切能分离的公共代码、资源
  • 无缝链接
    • 通过设定URI,宿主、本地化应用插件、本地化web插件、在线网页,以及任何自定义的插件之间能够相互调起与传递参数
  • 跨平台
    • 目前已支持Android、iOS以及html5插件。并且三者之间可以通过同一套javascript接口进行通信。
二、Small Android(支持平台:Android API 15(4.0.3))

各个插件框架功能对比:

  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动态注册 × × × × [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文件夹,导入。

Small sample

  • Sample 示例工程
    • app 宿主工程
    • app.* 包含Activity/Fragment的组件
    • lib.* 公共库组件
    • web.* 本地网页组件
    • sign 签名文件

顺便说下,这些app.*跟web.*可以从工具栏的▶️按钮单独运行。
其中app.home无法单独运行是因为它只包含一个Fragment,没有Launcher Activity。

Step 3. Build libraries (准备基础库)

> [./]gradlew buildLib -q (-q是安静模式,可以让输出更好看,也可以不加)

Build libraries

Step 4. Build bundles (打包所有组件)

> [./]gradlew buildBundle -q (-q是安静模式,可以让输出更好看,也可以不加)

Build bundles

步骤3跟4,如果你喜欢,也可以在Gradle任务导航里运行
Small tasks

单独编译一个组件可以使用 [./]gradlew -p web.about assembleRelease
或者
Sub tasks

Step 5. Import DevSample project (导入开发工程)

打开Android Studio,File->New->Import Project... 选择DevSample文件夹,导入。

Small devsample

  • DevSample 开发工程
    • buildSrc 组件编译插件,用于打包组件
    • small 核心库,用于加载组件

buildSrc在修改后会被自动编译。

其他步骤同上。除了编译单个组件的命令有所不同:

> [./]gradlew :app.main:assembleRelease

P.s. gradlew命令支持缩写,比如assembleRelease可以缩写为aR


四、实现Android插件化的核心技术:
  1. 插件是否一定得内置到APK中,如果一定要内置,那这个框架仅仅是为了开发工程中解耦?

    内置是为了首次启动更快,如果不内置也行,可以在启动页下载插件。

  2. 目前开源的插件开发框架有很多,Small 跟这些框架的区别是什么,优缺点呢?

    1. 百度工程师开发的 Dynamic-load-apk
    2. melbcat开源的 Direct-Load-apk
    3. limpoxe开源的 Android-Plugin-Framework
    4. bunnyblue开源的 ACDD
    5. 携程工程师开发的 DynamicAPK
    6. 360工程师开发的 DroidPlugin
    1. dynamic-load-apk使用代理的方式实现Activity生命周期,插件中不能用this,不够透明;
    2. Direct-Load-apk对dynamic-load-apk进行了改进,支持this。但也是用代理Activity,实现较为繁琐;
    3. Android-Plugin-Framework是一个相对完整的框架,但资源分区方案还不够理想,不支持加载.so插件;
    4. ACDD 使用了osgi,没有细看。。坑点是资源分区要使用修改aapt源码再重新编译的方案;
    5. DynamicAPK 坑点:修改aapt源码,不支持.so插件;
    6. DroidPlugin支持对完整apk的动态加载,但是没有关于非独立插件的加载(资源分区要复杂得多);
    7. 这些框架似乎都不支持AppCompat包(但这很重要,材料设计的Design包等都依赖AppCompat);
    8. Small的开发其实是跟随1、2、3走过来的。从实际场景出发,基于“轻量、透明、极小化、跨平台”的理念:
      • 把核心代码量控制在了一个文件(ApkBundleLauncher)500行以内
      • 不修改aapt源码,实现了资源id PP段的再分配(原理见Dynamic load resources
      • 通过对aapt生成的二进制文件的后期加工,最大化分离无用的资源,使得插件包最小达到4k左右
      • 支持对本地化网页进行插件打包,实现跨平台
  3. 目前大多插件框架对Service的支持不太好,Small是否支持,支持度怎么样。

    支持动态加载,不支持动态注册。实际场景中,Service的更新频度较低,注册可以预埋在宿主的manifest文件中。

  4. 插件与宿主必须运行在同一个进程吗? 是否支持插件运行在单独的进程中。

    在同一个进程。

  5. 当插件与宿主共有一个控件时(比方说自己写了一个下拉刷新的控件),开发过程中是不是要插件和宿主里面都含有这个控件? 同样,是否允许插件访问宿主的资源(比如说res)以及数据(SharedPreferences中的数据)。

    插件中所有的公共资源、类将会被分离。宿主、各个插件之间的资源可以互相访问,支持SharedPreferences,完全透明。


猜你喜欢

转载自blog.csdn.net/rongwenbin/article/details/51882225