专业 靠谱 的软件外包伙伴

您的位置:首页 > 新闻动态 > Android系统软件开发之dex多分包架构设计

Android系统软件开发之dex多分包架构设计

2016-09-14 14:11:37

自2015年QQ空间Team放出安卓App热补丁动态修复技术介绍之后,各种热修复技术层出不穷,越来越多的项目也开始尝试这种新技术,也有了一些相对稳定的框架出现。本文主要讲解如何在Eclipse中利用Ant构建工具产生多个dex文件,从而为热修复做准备。若有问题恳请指正,欢迎评论交流!

 

这里写图片描述

 

1、什么是dex多分包?

一般情况下Apk解压后里面都会包含一个classes.dex文件,该文件里面包含了应用的所有.class文件,当创建多个dex文件,并指定某些.class到指定dex文件中就是dex多分包。简言之,一个Apk包含多个.dex文件。

dex多分包的应用场景:

  • 解决方法数越界,因为单个dex文件中方法数最多为65536个。超出将抛出DexIndexOverflowException。(方法包括:自定义方法,FrameWork及第三方Jar的所有方法,可通过IDA Pro逆向工具进行查看)。
  • 实现热修复,该热修复根据QQ空间Team的热补丁动态修复技术原理进行修复。

2、dex多分包的基本步骤

在ADT中对Android项目进行多分包需要借助Ant构建工具,通过修改构建策略和规则产生多分包,如果你对Ant构建不是很了解可以参考http://blog.csdn.net/magic_jss/article/details/52504131;该步骤讲解主要是根据前篇文章的修改。

Ant打包主要流程:

  1. 删除gen、bin目录并新建
  2. 生成R.java类文件
  3. 编译aidl文件
  4. 编译源文件生成对应的class文件
  5. 将.class文件转化成.dex文件
  6. 打包资源文件
  7. 生成未签名的apk包
  8. 对未签名的apk包进行签名
  9. 对签名的apk包进行字节对齐

在第5步,将.class文件转化成.dex文件,在这里可以修改打包策略产生多个dex文件,修改如下: 
主包:classes.dex主要存放Activity、Application等入口类。 
从包:classesn.dex主要存放业务逻辑类,可能出现bug的类,后期修复的类。

    <!-- 将.class文件转化成.dex文件 -->
    <target
        name="dex"
        depends="compile" >

        <echo message="dex..." />

        <exec
            executable="${dx}"
            failonerror="true" >

            <arg value="--dex" />

            <arg value="--multi-dex" /><!-- 多分包命令 -->

            <arg value="--set-max-idx-number=10000" /><!-- 指定单个dex中的方法数 -->

            <arg value="--main-dex-list" />

            <arg value="${basedir}/c.txt" /><!-- 放到主包里面的class文件 -->

            <arg value="--minimal-main-dex" />

            <arg value="--output=${bin}" /> <!-- 输出位置 -->

            <arg value="${bin}" /><!-- 把bin下所有class打包 -->

            <arg value="${libs}" /><!-- 把libs下所有jar打包 -->
        </exec>
    </target>
  • 		1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

c.txt  将该文件里面的class文件打包到classes.dex(主包),一般情况下要保证该文件里面的类不会出现问题。

com/magic/test_hotfix/MainActivity.class
com/magic/test_hotfix/MainActivity$1.class//代表内部类
com/magic/test_hotfix/HotFixUtils.class
  • 		1
  • 2
  • 3

在第7步,需要将主包classes.dex添加到未签名的apk中去。

    <!-- 根据classes.dex文件和resources.ap_生成未签名的apk包 -->
    <target
        name="package"
        depends="package-res-and-assets" >

        <echo message="package..." />

        <exec
            executable="${apkbuilder}"
            failonerror="true" >

            <arg value="${unsigned-package}" /><!-- 输出 -->

            <arg value="-u" /><!-- u指创建未签名的包 -->

            <arg value="-z" /><!-- 资源压缩包 -->

            <arg value="${resources-package}" />

            <arg value="-f" /><!-- dex文件 -->

            <arg value="${bin}/classes.dex" />

            <arg value="-rf" />

            <arg value="${src}" />

            <arg value="-rj" />

            <arg value="${libs}" />

            <arg value="-nf" />

            <arg value="${libs}" />
        </exec>
    </target>
  • 		1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

由于aapt命令在添加或者删除的时候不能是绝对路径,而是相对路径,因此需要拷贝文件到项目的根目录下面,因为我们的脚本是在根目录下面,这样在运行aapt的时候,可以直接操作dex文件了。

<!-- 拷贝文件到项目的根目录下面,因为我们的脚本是在根目录下面,这样在运行aapt的时候,可以直接操作dex文件了 -->
    <target
        name="copy_dex"
        depends="package" >

        <echo message="copy dex..." />

        <copy todir="." >

            <fileset dir="${bin}" >

                <include name="classes*.dex" />
            </fileset>
        </copy>
    </target>
  • 		1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

添加其他dex文件到未签名Apk中去。这里需要用到ant-contrib-1.0b3.jar,下载地址http://download.csdn.net/detail/magic_jss/9628968;将该Jar放到Ant解压目录的lib目录下面,还需要在build.xml中指定ant-contrib-1.0b3.jar的路径。

    <!-- ant-contrib-1.0b3.jar的路径-->
    <taskdef
        classpath="D:\Ant\apache-ant-1.9.7\lib\ant-contrib-1.0b3.jar"
        resource="net/sf/antcontrib/antlib.xml" />
  • 		1
  • 2
  • 3
  • 4
    <!-- 使用aapt命令添加dex文件 -->
    <target name="aaptadd-dex" >

        <echo message="${dir.name}" />
        <!-- 使用正则表达式获取classes的文件名 -->
        <propertyregex
            casesensitive="false"
            input="${dir.name}"
            property="dexfile"
            regexp="classes(.*).dex"
            select="\0" />
        <!-- 这里不需要添加classes.dex文件 -->
        <if>
            <equals
                arg1="${dexfile}"
                arg2="classes.dex" />

            <then>
                <echo>
                   ${dexfile} is not handle
                </echo>
            </then>

            <else>
                <echo>
                    ${dexfile} is handle
                </echo>

                <exec
                    executable="${aapt}"
                    failonerror="true" >

                    <arg value="add" />

                    <arg value="${unsigned-package}" />

                    <arg value="${dexfile}" />
                </exec>
            </else>
        </if>

        <delete file="./${dexfile}" /><!-- 删除项目根目录下dex文件 -->
    </target>
  • 		1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

其他步骤就是对apk进行签名,对签名的apk包进行字节对齐,和之前文章一样不在赘述。

最后通过cmd进入到该项目目录下执行 ant make (make为自定义)命令即可进行打包。由于项目中我只打包了两个dex,即classes.dex、classes2.dex,查看dex文件内容如下:  classes.dex 

这里写图片描述

 

classes2.dex 

这里写图片描述

 

3、完整的build.xml脚本

<?xml version="1.0" encoding="UTF-8"?>
<project
    name="Test_HotFix"
    default="make" >

    <property file="local.properties" />

    <property file="ant.properties" />

    <property environment="env" />

    <condition
        property="sdk.dir"
        value="${env.ANDROID_HOME}" >

        <isset property="env.ANDROID_HOME" />
    </condition>

    <taskdef
        classpath="D:\Ant\apache-ant-1.9.7\lib\ant-contrib-1.0b3.jar"
        resource="net/sf/antcontrib/antlib.xml" />

    <!-- 相关目录 -->
    <property
        name="basedir"
        value="." />

    <property
        name="sdk-tools"
        value="${sdk.dir}/tools" />

    <property
        name="sdk-build-tools"
        value="${sdk.dir}/build-tools/android-4.4.2" />

    <property
        name="android-jar"
        value="${sdk.dir}/platforms/android-19/android.jar" />

    <property
        name="framework-aidl"
        value="${sdk.dir}/platforms/android-19/framework.aidl" />

    <!-- 编译工具 -->
    <property
        name="aapt"
        value="${sdk-build-tools}/aapt.exe" />

    <property
        name="aidl"
        value="${sdk-build-tools}/aidl.exe" />

    <property
        name="dx"
        value="${sdk-build-tools}/dx.bat" />

    <property
        name="apkbuilder"
        value="${sdk-tools}/apkbuilder.bat" />

    <返回首页] [打印] [返回上页]   下一篇