Multidex

作者: gogoingmonkey | 来源:发表于2018-04-18 14:33 被阅读43次

    Multidex

    这个插件相信大家使用的不少,今天说下他的原理,以及最近遇到的一个问题。

    实现原理

    实现原理是将class编译进不同的classes.dex文件中,一般情况下,一个APK文件中只包含了一个classes.dex文件。分包之后就存在一个主的classes.dex,多个副的classes2.dex,classes3.dex,以此类推。


    image.png

    在要启动程序时,Android会先去加载主的classes.dex(这是系统自己加载的),然后在程序启动后再去加载其它副的dex(比如MultiDex.install)。

    Multidex的编译

    整个过程,它由三个不同的gradle task组成

    1、collect{variant}MultiDexComponents task

    这个task会读取项目的AndroidManifest.xml文件中注册的application、Activity、service、receiver、provider、instrumentation相关类,并将其class文件路径写到文件buidl/intermediates/multi-dex/${variant.dirName}/manifest_keep.txt中

    2、shrink{variant}MultiDexComponents task

    这个task会调用ProGuard并根据上一步生成的manifest_keep.txt文件内容去压缩class,剔除没有用到的class,生成一个精简的jar包buidl/intermediates/multi-dex/${variant.dirName}/componentClasses.jar

    3、create{variant}MainDexClassList task

    这个task会根据上一步生成的componentClasses.jar去寻找这里面的各个class文字中依赖的class,比如一个class中有一成员变量X,那么X就是依赖的class,componentClasses.jar中所有的class和依赖的class路径都会被写入到文件buidl/intermediates/multi-dex/${variant.dirName}/maindexlist.txt中,这个文件中的类都会被编译进主的classes.dex中去

    对于一个使用了MultiDex的Android工程,编译后在/build/intermediates/multi-dex/{variant_path}/路径下面,可以看到如下几个文件


    image.png

    从他的整个编译流程可以得到结论:精简主dex的大小在于精简manifest_keep.txt的大小,进而减小jar的大小,进而减小maindexlist.txt的大小

    manifest_keep.txt大小的控制

    一方面是从业务下手,减小application、Activity、service、receiver、provider、instrumentation相关类的依赖,特别对第三方SDK的依赖,但是这里对业务要求比较高,比较容易因为业务人员的改动造成分包失败,所以放到下一阶段再做; 那么就只能从gradle task 任务入手,对collect task任务进行拦截和替换,代码如下(在app/build.gradle最尾部加入如下代码)

    afterEvaluate {
    android.applicationVariants.each {
    variant ->
    def collectTask = tasks.findByName("collect${variant.name.capitalize()}MultiDexComponents")//collectZroTestDebugMultiDexComponents
    if (collectTask != null) {
    List<Action<? super Task>> list = new ArrayList<>()
    list.add(new Action<Task>() {
    @Override
    void execute(Task task) {
    println "collect${variant.name.capitalize()}MultiDexComponents action execute!---------XXXXXXX mini main dex生效了!!!!$projectDir"
    def dir = new File("$projectDir/build/intermediates/multi-dex/${variant.dirName}");
    if (!dir.exists()) {
    println "$dir 不存在,进行创建"
    dir.mkdirs()
    }
    def manifestkeep = new File(dir.getAbsolutePath() + "/manifest_keep.txt")
    manifestkeep.delete()
    manifestkeep.createNewFile()
    println "先删除,后创建manifest_keep"
    def backManifestListFile = new File("$projectDir/manifest_keep.txt")
    backManifestListFile.eachLine {
    line ->
    manifestkeep << line << '\n'
    }
    }
    })
    collectTask.setActions(list)
    }
    }
    }
    
    
    

    完成代码同步后一般会报错,因为少了个文件,这个时候需要自己手动创建一个文件,在本地的projectDir/manifest_keep.txt配置最精简的主dex所需要依赖的类

    -keep class com.meiyou.framework.biz.ui.LoadResActivity { <init>(); }
    -keep class com.lingan.seeyou.messagein.NotificationTranslucentActivity{ <init>(); }
    -keep class com.j256.ormlite.field.**
    -keep class com.lingan.seeyou.ui.application.AppShell{
    <init>();
    }
    -keep class com.tencent.tinker.loader.** {
    *;
    }
    -keep class com.lingan.seeyou.ui.application.TinkerApp {
    *;
    }
    -keep class com.lingan.seeyou.ui.application.SeeyouApplication {
    *;
    }
    -keep public class * implements com.tencent.tinker.loader.app.ApplicationLifeCycle {
    *;
    }
    -keep public class * extends com.tencent.tinker.loader.TinkerLoader {
    *;
    }
    -keep public class * extends com.tencent.tinker.loader.app.TinkerApplication {
    *;
    }
    

    只保留了application和启动页之类的入口;用这种优化确实可以减小一部分数量,我们也因此继续开发了半年多的时间,然而好景不长,近两个版本加入的SDK过多,导致application和lanucher页面直接依赖的方法是成功超越65535,再次暴出了too many classes in --main-dex-list;因此我们需要再进一步优化(业务裁剪是在难上加难)

    进一步优化

    最终决定主dex大小的,是最后一个maindexlist.txt文件大小,这个文件列出了所有在主dex需要的类,如果我们能裁剪这个文件的大小,就可以将主dex精简下来; 但是gradle task只能拦截collect 这样的任务,无法替换maindexlist.txt内容,我们也暂时没有办法写依赖分析脚本来分析哪些是要放在主dex,哪些是不需要的;于是找到了一个可以干预maindexlist.txt文件内容的项目DexKnifePlugin
    这个插件具体使用可以在网上找下,因为我通过第一波操作已经不报错了,等业务不忙了在研究这个,据我了解,使用这个的情况还是不多的,除非项目特别大了,一般都到不了这个程度。
    然解决其启动的问题,但是启动速度依然还是很慢,根本原因在于MultiDex install的时间太久,主dex太大,所以接下来的优化方向会是:

    精简业务入口(application,service,receiver等系统组件)

    对业务改动要求比较高;它是系统自己加载的主dex需要的数据,这一步需要一个依赖分析的脚本,来确保第一个主dex加载完成之后,我们启动到主页的情况下,能够索引到所有被引用的类,否则很容易出现ClassNotFound;

    需要编写自定义MultiDex,来加载其余的dex,需要处理各个安卓版本MultiDex的差异;处理第N个Dex未加载完成之前,用户点击了非主dex的页面的时候或者引用到非主dex的类的时候,如何防止因ClassNotFound导致的闪退

    相关文章

      网友评论

          本文标题:Multidex

          本文链接:https://www.haomeiwen.com/subject/wxbvkftx.html