Android 多Dex分包机制

来源:互联网 发布:战舰世界腓特烈数据 编辑:程序博客网 时间:2024/06/04 20:26
前言        随着项目工程越来越庞大,代码的方法数不断增长到一定程度,就出现Android 低版本系统应用无法安装的情况。那么这是哪里出错了?Android系统对安装包有哪些限制?


1问题描述


        前一阵子,我们发现公司的某一个业务,在Android 2.3及系统安装不了。此时,我们该业务的Android客户端开发已经有50个人。一般外面公司的Android开发也就2~3个,代码的体量也很难增长到像我们这样的规模。但任何一个大项目都会碰到该问题。

        我们来看一下,出现问题时,开发工具会有如下的提示:

Conversion to Dalvik format failed:
Unable to execute dex: method ID notin[0,0xffff]:65536

或是

trouble writing output:
Too many field references:131000; max is65536.
You may tryusing--multi-dex option.

        上述两个错误提示不一样,但问题都是同一个问题。只是开发工具的编译系统版本不同,提示不同。可以看出,是某一项达到系统规定的阀值,导致安装失败了。


2问题定位


        从上述的提示来看,里面有一个数字65536很抢眼,这个正好是Java int的最大值。由于Android代码是以dex形式的存在,那么dex在系统里是如何定义的呢?

        首先,每一个Dex文件,都会有这么一个头部信息DexHeader:


        从上面可以看出,在头部信息里面分别定义了filed(字段)、method(方法)、classDefs(类)等的数量,这个数量是u4类型。至于u4类型的定义是


        可以从上面的定义看出,u4类型就是uint32_t,而uint32_t正好是无符号整型2^32=65536。因此,这也就不难解释,为什么dex文件超过这个限制后,就出现无法安装的情况。

        一般情况下,dex的方法数最多,因此很容易达到65536这个数量限制,而字段和类则比较难。所以,出现问题的都是方法超出居多。

        


3Dex引用构成


        一个Dex文件,它的引用组要由下面三部分组成。

        因此,dex引用的方法数不仅仅和自己编写的代码有关,还和引用系统的方法数、第三方集成库的方法数有关。因此,方法数超标,也是多方面的原因构成的。

        想看自己dex文件的方法数,可以搜索dex-method-counts获得自己dex数量。


4解决思路


        从上面的分析可以看出,出问题的原因是因为单个Dex引用的方法数超标了。那么我们是否可以拆分多个dex,从而避免单个dex的方法数超标?答案当然是可以的。

        目前,为了解决dex方法数超标的问题,有三种主流思路:代码瘦身、插件化和分dex。这三种解决方案可以结合使用,效果更好。


1

代码瘦身

        

         代码瘦身,可以从自己定义/引用的方法和第三方引入的库下手。

        通过review自己的代码,减少方法数的定义(去掉一行函数,比如get和set,一般都是一行的,可以直接对变量做引用)、合并方法、减少对外部的依赖,从而减少最终dex引用的限制。

        第三方引用库,一般一个引用jar包里面,有很多方法其实我们是引用不到的,我们可以对这些jar包做瘦身(jar是压缩包,里面都是class文件,我们去掉一些不相关的class,从而减少jar包大小),从而减少最终dex的引用限制。

        同时,可以引入Proguard工具,开启Proguard的代码瘦身功能,会自动帮你删除无用的代码,最终生成的dex文件也会变小的。


2

插件化

  

        插件化,也就是对程序中独立的模块做成插件,从而减少主dex文件的大小。插件化的思路其实就是对dex做拆分,从而使主dex变得更小,插件则是以独立的dex存在。

       但插件化需要自己开发一套插件化的框架,成本较高,而且只有独立的模块才适合做插件。很多时候,我们的代码存在很多引用,很难拆成独立的模块。

       因此,插件化,是无法从根本上解决这个问题。 


3

分Dex


        上述方案都不能从根本上解决问题,可以采用分dex的方案,适用范围更广。

        分dex是将dex分成多个dex,从而避免单个dex的引用超过限制,分dex的方案不需要关心独立性问题,而且Android Studio开发工具已经支持这项能力,使用起来,成本也很低。


5分Dex实现

        

        Gradle的Android插件,从SDK build tools 21.1或是更高的版本就支持多dex的能力,需要自己手动配置一下。步骤如下:


5.1开启分包功能

        在build.gradle编译的配置文件里面引入分包依赖库和开启分包功能。如下所示:

        

android {
    compileSdkVersion 
21
    buildToolsVersion 
"21.1.0"

    defaultConfig 
{
        
...
        minSdkVersion 
14
        targetSdkVersion 
21
        
...

        
// Enabling multidex support.
        multiDexEnabled 
true
    
}
    
...
}

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

        

5.2修改Application入口


        一种方案是,直接修改AndroidManifest,使用MultiDexApplication,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    
package="com.example.multidex">
    
<application
        ...
        
android:name="android.support.multidex.MultiDexApplication">
        ...
    
</application>
</manifest>

          另一种方案, 如果自己有自定义的Application,则可以重写attachBaseContext()方法,在里面调用MultiDex.install(this) 来开启分dex功能。


        当所有的配置OK后,Android编译工具会自动生成一个主dex(classes.dex),和其它附属dex(classes2.dex,classes3.dex)。当然这个附属dex是根据需要来会生成。


6分Dex局限性


        虽然分Dex,可以解决dex方法数限制问题,但开发必须关注以下问题:


6.1附属包过大

        

         附属包如果过大,可能导致应用启动时发生ANR。所以,需要使用Proguard来做代码瘦身,减少附属包的大小。


6.2低版本运行问题


        在Android 4.0以下(API level 14以下),可能出现运行不起来的问题,原因是Dalvik linearAlloc的bug。同样通过proguard可以瘦身代码,避免这个问题。


6.3内存消耗问题


        使用多dex方案,会导致应用请求更多的内存空间,从而出现crash。原因同样来自Dalvik的linearAlloc的内存分配限制。虽然在Android 4.0上已经提高了内存分配限制,但仍然还是很有可能达到这个限制。


6.4代码分包的复杂性


        虽然,分包解决了dex引用限制的问题,但是由于dex内部复杂的引用,所以,在对代码分包时,必须考虑到启动时就需要用到的,都必须放到主dex中。

        目前编译开发工具还不支持指定class必须放到主dex中,后续开发工具会逐步完善并支持该功能。当然,如果你觉得有必要控制哪些代码必须在主dex里面,可以自己编写编译脚本。

0 0
原创粉丝点击