Android插件化技术介绍

插件

Android插件化的目的主要有两个,第一个是应对每个dex包65536个方法数的上限问题,第二是实现功能复杂的app的拆解,能够按需下载和加载运行所需的模块。插件化的实现并没有统一的标准或方式,很多公司都有自己的一套插件化方案,而且没有哪个公司的方案被业界普遍认可。本文将按时间轴的方式介绍几家比较有代表性的公司的插件化方案,供大家开阔思路。至于要选取那种方案为我所用,那就只有结合自己的实际需求实践比较得知。

Facebook

Facebook是进行插件化开发尝试的鼻祖,早期Facebook安装包变大以后出现的两个导致安装失败的问题:
1. Number of Java methods in our app more than 65536(64K), can‘t hold in one dex file.
2. Large number of methods was exceeding “LinearAlloc” buffer and causing dexopt to crash
第一个错误是说方法数目超过最大数目64K,这是因为Android每个Dex文件的方法通过两个字节进行索引,两个字节能索引的最大数目就是64K。第二个错误是说单个dex包大小超出了Dexopt所能使用的最大缓存。Dexopt是将.dex文件进行优化,并转化成.odex的工具,所能使用的最大缓存跟手机相关,一般为8M或16M,如果dex包太大就有可能超出Dexopt所能使用的最大缓存,导致出错。
对此Facebook采取了两种方法来解决以上问题,总结如下:
1. 将安装包拆分成多个dex文件,运行时通过DexClassLoader动态载入
2. 找到Delvik虚拟机的缓存设置代码片段,替换成更大的缓存

Google

后来,Google在Android里引入这种问题一的解决方法,MultiDex方法,并将其标准化(5.0以下版本,buildtools升级到21即可使用支持包)。使用步骤:

首先修改gradle文件,在defaultConfig里面添加multiDexEnabled属性,并修改dependencies:

android {    
    ...
    defaultConfig {        
        ...        
        minSdkVersion 14        
        targetSdkVersion 21        
        ...        
        // Enabling multidex support.        
        multiDexEnabled true    
    }    
    ...
}

dependencies {  
    compile 'com.android.support:multidex:1.0.0'
}

然后在Application的扩展类中重写attachBaseContext函数:

public class MyApplication extends Application {
    ...
    @Override
    protected void attachBaseContext(Context base) {    
        super.attachBaseContext(base);    
        try {        
            // to resolve: java.lang.NoClassDefFoundError        
            MultiDex.install(this);    
        } catch (Exception e) {        
            e.printStackTrace();    
        }
    }
    ...
}

该方法大部分情况下可行,但是存在几个问题:
1. 理论上该方法只解决了64K的问题,如果拆分后的包超出了LinearAlloc的大小(Dalvik linearAlloc bug),还是会触发问题二导致crash
2. 点击图标启动的时候既要对主dex文件进行dex的解压、dexopt、加载操作,又要对辅dex文件进行dex的解压、dexopt、加载操作,容易导致ANR错误,导致首次启动初始化失败
3. mulitDex的App有可能在4.0以前的机器上无法启动,因为Dalvik linearAlloc bug
4. 哪些class被放在主dex文件,哪些在辅dex文件可以由build tools 搞定,但是如果你代码存在反射和native的调用也不保证100%正确

有些技巧可以缓解存在的上述问题:
1. 尽量减少无用依赖
2. 使用Proguard进行代码瘦身
3. 通过gradle里--set-max-idx-number=48000参数减小每个dex最多容纳的方法数,写的小一点可以多产生几个dex文件

美团网

美团网对拆分dex包的过程进行了干预,并针对ANR错误进行了优化,他们要解决的三个问题以及采取的方案如下:
1. 如何控制生成多个dex包
a. 在gradle中定义一个task,控制生成多个dex包
2. 如何控制哪些class放到主dex包,哪些放在辅dex包
a. 把Service,Receiver和Provider以及依赖的class都放在主dex包中。Activity则进行区别对待
b. 把首页Activity,欢迎页Activity等需要首先展现的Activity及依赖的class放到主dex包中,把二级,三级页面及以来的
class放到辅dex包中
3. 如何避免动态加载辅dex包导致的ANR错误
a. 开启一个线程异步加载辅dex包。这样做潜在的问题是,在辅dex包加载完成前,如果启动了辅dex包里的Activity,会导致
ClassNotFound的错误。所以在所有Activity启动前要做一个校验,如果试图打开Activity时辅dex包还没有加载完成,则给个
正在加载的提示给用户。他们研究发现Activity是通过ActivityThread的Instrumentation来启动的,其中跟Activity相关
的方法包括execStartActivity,newActivity方法,所以在这两个方法里加入了校验过程,如果还没加载完就给用户展示个正
在加载的提示

微信

微信团队也总结了一套自己的优化方案,他们针对动态加载辅dex包进行了自己的处理,微信主要修改了动态加载辅dex包的方式:

第一次加载辅dex包时需要进行dexopt操作,这个过程比较耗时,但是如果已经加载过一次再加载就会很快了,所以微信的策略是,第一次加载的时候在sharedpreference中写个标记,下次启动如果发现已经有该标记,直接往下走,否则在新开一个透明的Activity,这样这个Activity就是前台进程,主进程就不再是前台进程,也就不会出现ANR错误了。流程如下:

微信动态加载Dex文件流程

携程网

携程网为了实现插件化并行开发,借鉴前辈们的思路开发了一套自己的插件化方案DynamicApk,他们不仅实现了dex包的拆分和动态加载,更重要的是,他们实现了多个插件apk独立并行开发,以便多人协作开发。
面临的问题及解决思路:
1. 编译期:资源和代码的编译
a. 每个apk指定不同的标志位
b. 引用宿主base.jar
2. 运行时:资源和代码的加载
a. 访问不同的资源根据标志位去不同apk加载
b. pathList追加dex路径
实现思路:
1. 针对插件子工程做的编译流程改造
2. 运行时动态加载改造
实现方式:
1. 对aapt工具的修改
2. gradle打包脚本的实现
3. 运行时加载代码的实现

上面介绍的几家公司的插件化技术可以分为两类:多dex方案和多apk方案。这两种方案异同总结如下:
1. 相同点
a. 都可以把一个大工程拆分成多个组件(多个.dex文件或多个apk文件)
b. 都可以做到使用哪个功能就按需动态加载那个组件
2. 不同点
多dex文件方案
a. 需要各个小组在一个工程下开发
b. 所有代码合起来编译,编译生成一个apk包,该apk包含多个dex文件
c. 各个dex文件只包含类文件,不包含资源文件
d. 所有类一起编译,编译完了再拆分成不同的dex文件
e. 运行时,资源都从主dex文件加载;代码加载靠往pathList上添加dex路径
f. 编译时间长
多apk的方案
a. 每个组有自己独立的工程
b. 每个组各自编译生成自己的apk包
c. 各个apk包都既包含资源文件,又包含类文件
d. 各个apk包要考虑资源怎么编译(分配不同的标志位),代码怎么编译(引用宿主base.jar)
e. 运行时,资源的加载根据不同的标志位去不同apk中加载;代码加载靠往pathList上添加dex路径
f. 编译时间短

两种方案如何选择:
1. 多dex方案不需要额外的编码规范,但是需要人工控制拆包的过程,成本略低
2. 多apk方案可以节约很多编译时间,使用该框架需要遵循一定的编码规范,成本稍高
3. 多dex方案成本低,编译时间长,多apk方案成本高,编译时间短
4. 实践来检验谁更好

其他技术方案

360的 DroidPlugin开源框架
    1. 可以在不对插件apk做任何修改的情况下运行没有安装在手机上的apk,因为绕过了用户授权,颇具流氓色彩,被成为安卓黑科技
    2. 代码很乱,实用性有限
Xposed
    1. 可以Hook所有app的函数或系统函数
    2. 实现系统或app层级的特效

参考:
https://www.facebook.com/notes/facebook-engineering/under-the-hood-dalvik-patch-for-facebook-for-android/10151345597798920

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

推荐阅读更多精彩内容