主流插件式框架探究

前言
目前移动端产品功能越来越复杂,模块不断增加,APK体积也不断增长。由于Android Dalvik最初设计的问题,单个.dex文件方法数最多是65536个。因此,APK难免会遇到64K方法数限制的问题。
Google官方提供了MultiDex解决方案。但是该解决方案,有比较大的局限性。

应用进程启动时MultiDex需要在主线程去做DEXOPT操作,中间涉及到文件读写、文件验证、数据复制、反射调用等,非常耗时,应用启动将变得很慢,甚至出现ANR。

由于Dalvik LinearAlloc的BUG,Android 4.0(API level 14)之前的系统,可能导致应用启动失败。应用上线前,需要针对低版本系统做严格测试。另外Android 5.0(API level 21)之前的系统,Dalvik加载类的堆内存的大小有限制,可能导致使用了MultiDex的应用在分配内存时,会出现崩溃。

引入MultiDex时,会存在多个dex文件,应用启动时,先加载了主dex文件,其它的dex文件在Application的attachBaseContext()方法中加载。Android构建工具帮我们分析处理了一部分依赖,将应用启动需要的类都放入了主dex文件中。但是项目中我们自己引入的第三方库或者底层的native代码,可能还有一些类的依赖构建工具无法处理,会导致应用启动时找不到相关类而崩溃。

综上所述,MultiDex方案并不完善,将直接应用启动变慢和崩溃的问题。为了解决这些问题,我们引入插件的概念。

技术关键点
插件框架的基本形式就是将一个apk中的不同功能模块进行拆分,打包到不同的dex或者apk文件中,而主工程是一个提供了加载模块dex和apk的框架。插件框架需要着重解决如下几个问题:

代码的动态加载。Android系统提供了DexClassLoader,可以用来动态加载apk、dex和jar文件。加载顺序决定了起作用的类。这一点可做用于热修复功能。

四大组件的动态注册和生命周期管理。通过插桩的方式做动态代理。可以在多个层级做Hack,不同的层级Hack时需要做的适配工作难度也不同。一般都需要反射替换Instrumentation,并对一些关键方法使用动态代理,以达到“欺上瞒下”的目的。

开源框架简介
目前,已经有多种插件框架的实现方案,不同的方案都存在各自的优缺点。以下简单介绍几个主要的插件框架的基本原理和优缺点。

DroidPlugin
DroidPlugin是360手机助手实现的一种插件框架,可以直接加载运行第三方的独立apk,不需要对apk进行修改或安装,插件 apk可以独立运行。其特性主要有以下几点:

通过Stub插桩的方式,预先静态注册多个不同属性的Activity、Service、Receiver和Provider,动态代理。一个插件一个ClassLoader,插件之间代码完全隔离,互不影响。
资源动态加载。一个插件一个AssetManager,插件之间资源完全隔离,互不影响。
Hack了几乎所有的通过Context获取到的系统服务(AM、PM等),Hack了和应用进程密切相关的Instrucmentation、ApplicationThread、ActivityThread等相关类。框架中很大一部分代码都是与Hack有关。
不同的插件APK运行于不同的进程,互不干扰。插件之间支持aidl进程间通信。提供进程管理机制。
存在局限的地方:

由于进程隔离的原因,宿主和插件分别使用了不同的ClassLoader和AssetManager,插件和宿主、插件和插件之间资源和代码不能共享。
插件apk中所有的Intent Filter无法工作。要启动插件中的四大组件,必须要指定包名和类名。
RemoteView支持不是太好,不支持自定义资源的Notification。
带有native库的插件支持不是太好,可能存在异常崩溃。
Hack了很多系统类,兼容性方面可能会隐患较多。
综合以上信息,DroidPlugin比较适合主程序只提供入口,而插件apk对宿主不存在依赖,插件之间无太多通信要求的场景。

Small
Small的核心是轻巧,支持跨平台(支持Android、iOS、Web)。其特性主要有以下几点:

轻度Hack,代码相对很少,逻辑简单。插件可以是apk文件、so库、lib库。
提供了Gradle插件辅助构建项目,修改各个插件资源id,避免资源id重复问题。
通过Stub插桩的方式,预先静态注册多个Activity,动态代理。
资源在编译时使用自定义gradle插件的方式区分开。多个插件共享进程,共享代码,共享资源。同一个ClassLoader加载所有插件。
使用uri的方式启动插件、传递参数,比较灵活。
基于Apache协议开源。
插件配置可以存放在服务端,demo中提供了简单的版本管理、在线更新机制。
其主要问题也比较明显:

目前不支持懒加载模式。所有插件都是在应用进程启动时一次性加载的,内存占用较大。如果要支持按需加载,需要自己做改进。
除了Activity,其它三大组件不支持插件化。作者没有支持其它三大组件的意向。插件中所有的Intent Filter无法工作。
插件更新后不是实时生效,而是在类被重新加载的时候生效。生效时间和类加载时机一致。
Small核心类代码凌乱,部分代码严重影响程序稳定性,还附带了部分业务逻辑,扩展性差。
综合以上信息,Small比较适合插件依赖于主程序,并且插件和主程序之间存在通信的场景,并且程序不宜规模太大。

DynamicAPK
DynamicAPK是携程的开源项目,但是目前作者已经不再维护。其主要特点如下:

同一个ClassLoader加载所有的插件代码,通过反射调整加载顺序,进而实现了热修复功能。一次性加载所有插件,未实现按需加载。
所有的插件资源都加载到了一个AssetManager中,并通过反射动态替换了Context获取的Resource。
Activity等组件需要在宿主程序中预先注册。
直接改造aapt的源码,通过动态配置的方式确定插件资源id的PP字段,避免id重复。
ACDD
ACDD的原名是OpenAtlas,目前作者也已经不再维护。携程的DynamicAPK有相当一部分代码借鉴了此项目。其主要特点如下:

自定义ClassLoader,扩展了双亲委派机制,用于加载插件代码。不同插件使用不同的ClassLoader,实现了代码的隔离,也实现了通用代码的共享。使用额外的配置文件管理插件和插件之间的依赖关系,实现了懒加载机制。
所有的插件资源都加载到了一个AssetManager中,并通过反射动态替换了Context获取的Resource。实现了懒加载机制。
Activity等组件需要在宿主程序中预先注册。
代码和相关文档中未提及资源id重复的问题是如何修正的。其内部的build tools则对所有插件和配置文件做了一个简单md5校验,未发现资源id相关的代码。应该是使用aapt打包时,指定了package id。
DynamicLoadApk
大概算是最早开源的插件化框架了,github上已经不怎么活跃了。其主要特点如下:

支持插件对主程序无调用、接口调用、完全调用3种集成方式。
支持Activity、FragmentActivity、Service组件,并且组件不需要在主程序的AndroidManifest中声明。
将Activity的生命周期抽象出接口,在代理类里实现对Activity生命周期的管理,并在代理类里保存原Activity的引用(that应用)。
DexClassLoader加载插件代码,AssetManager管理插件资源。插件之间资源和代码可以做到相互隔离。
自定义Intent和API实现对插件Activity的启动。
主要问题如下:

强侵入性,组件内很多API需要使用that调用,并需要继承特定的类,启动组件需要调用私有API。
插件的集成方式一旦确定将不能更改。不同的集成方式,编译配置也不相同。
插件之间资源和代码无法共享。启动插件中的组件必须使用包名+类名的方式。
VirtualApp
VirtualApp是一个App虚拟化引擎(简称VA)。VA在App内部创建一个虚拟空间,App可以在虚拟空间内任意安装、卸载、启动apk。而这一切都与外部隔离,如同一个黑盒。目前已经有较多的应用使用了VA,并支持多种方式的应用加固。其主要特性如下:

支持四大组件,插件中的静态注册的Receiver会转为动态注册。
插件和插件之间高度隔离,运行于独立的进程。
插件之间支持aidl通信,支持Intent隐式调用。
IO重定向,插件相关的文件存放于主程序私有文件目录下。
通过动态代理的方式,比DroidPlugin Hack了更多的系统类和方法,模拟了一个更完善的小型系统。
项目比较活跃,问题的解答和bug修复相对较快。
主要缺点如下:

Hack了很多系统类和方法,虽然比DroidPlugin稳定,但还是存在很多兼容性方面的问题。
使用GPL3.0的授权,有开源感染的风险。
插件隔离的原因,不支持代码和资源的共享。
Atlas
Atlas是阿里在17年3月份才开源的,目前还处于快速迭代开发期,文档和demo都还不完善,部分细节的功能还有不少的BUG。

Atlas主要的特性有以下几点:

单进程运行,更像一个组件框架,包含host、local bundle和remote bundle。
所有bundle的代码和资源都是动态加载,并且支持按需加载。
自定义两个ClassLoader,一个实现类加载时的路由逻辑,另一个则主要负责加载bundle中的类和其依赖的bundle中的类。
自定义ResourceManager,每个bundle里的资源都加入了同一个AssetManager。
支持差分升级和远程bundle调用。
提供了打包插件,编译期自动合并Manifest文件,自动分配bundle的资源package id,自动生成差分包,并提供单模块调试功能。
主要缺点如下:

项目刚开源,文档很少,主要功能不稳定。
差分升级和远程bundle调用,需要强有力的后台和测试支撑。
差分升级还不支持动态添加4大组件的功能。


作者:47045039
来源:CSDN
原文:https://blog.csdn.net/mountains2001/article/details/61196497
版权声明:本文为博主原创文章,转载请附上博文链接!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容