插件化Android开发Android技术知识

Android插件化技术介绍

2016-09-01  本文已影响330人  JackMeGo
插件

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

上一篇下一篇

猜你喜欢

热点阅读