android 65535 多dex文件方案

来源:互联网 发布:协同过滤推荐算法实例 编辑:程序博客网 时间:2024/06/04 18:03

快过年了,项目进度放换了,看android 热修复方案的时候,顺便把多dex拆分方案,看了一下。


1.前提执行备案

     方案需要大家了解只是:

  1.ant编译android项目

    step 1: 下载安装

        step 2: 跟android项目添加ant编译方式。参考   android update proejct -p ./

        step 3: 然后在项目的根目录中运行:ant debug 或者ant releces


  2.android 编译apk过程

  大概流程:

step 1. 收集所有的java文件,包括:R.java(通过android aapt生成),.java文件,.aidl文件(通过android AIDL 工具生成)


step 2.将生成的java文件,调用javac编译为.class文件


step  3.执行代码混淆


step 4.将所有的.class文件,通过android dex工具,打包为一个.dex文件()。


step 5. 将res,asset,dex,打包为apk


step 6. 签名


以上是android apk编译的大概流程。


2. 拆分dex原理:

        相关原来以及问题,个人感觉:android 多dex方案 整理的不错,就不在赘述。

3.实现方案

      整体方案参考 上面连接描述的。这个方案并没有自己重写android apk编译流程的build.xml文件。只是将android 原来的(%ANDROID_HOME%/tools/ant/build.xml,复制到项目中,将原来的文件做了些小的添加,

     整体方案如下:

     1.在android 编译流程中 step 3代码混淆执行完成后,将需要拆分的class文件移动别的文件夹中

     2. 在android 编译流程中 step 4中,当主dex文件执行完成后,在将需要拆分的class文件执行一次dex

     3.在android编译流程中 step 5之前,将拆分出来的dex文件复制到asset/dex(路径可以随意指定)目录中

     4.编译结束后,将复制到asset/dex文件中的dex文件删除

     5. 在apk的Application的oncreate中通过,反射,将asset/dex文件夹中的所有dex文件,加载到ClassLoader中,见:MultiDex.java
    

     下面进入主题:

    
      step 1: 配置项目可以使用ant编译,参考上面ant使用
                   在生成的build.xml中添加:
                   

    <!-- 拆分dex,从build_apk.xml移动到这里 -->

    <propertyname="out.dir"value="bin"/>

    <propertyname="out.absolute.dir"location="${out.dir}"/>

    <propertyname="asset.dir"value="assets"/>

    <propertyname="asset.absolute.dir"location="${asset.dir}"/>




      step 2 :将(%ANDROID_HOME%/tools/ant/build.xml 复制到项目的跟目录下,名称可以随意命名
                  A.我将android默认的build文件修改为:build_apk.xml

                  B. 修改ant默认的build.xml最后一句:<import file="${sdk.dir}/tools/ant/build.xml" />为:<importfile="build_apk.xml"/>

                  C.定义代码混淆后执行的target,代码位置在<target name="-obfuscate">前面加入:
                        <!-- add by shehonghao ,拆分dex文件,开始-->
                        <!-- 代码混淆后执行 -->
                        <target name="-post-obfuscate"/>
                     <!-- add by shehonghao ,拆分dex文件,结束-->


                  D.在target:<target name="-obfuscate">的最后面加入:
                       <!-- add by shehonghao,拆分dex文件,开始-->
                      <!-- 代码混淆结束后,开始拆封class文件 --> 
                     <antcall target="-post-obfuscate"></antcall>
                     <!-- add by shehonghao,拆分dex文件,结束-->


  E.找到:<target name="-crunch">
                     在代码之前添加:
                      <!-- add by shehonghao ,拆分dex文件,开始-->
     <!-- 打包dex后执行 -->
<target name="-post-dex" />
                        <!-- 创建资源后执行 -->
                       <target name="-pre-crunch"></target>
                      <!-- add by shehonghao ,拆分dex文件,结束-->
 
                   
  
                   F.找到: <macrodef name="dex-helper">,在<dex executalbe="${ds}的之后添加:
                               <antcall target="-post-dex"></antcall>
      
                   

   

    step 3: 创建多拆分包build_multi.xml,并在build.xml中引入:

          

         文件内容:

<?xmlversion="1.0"encoding="UTF-8"?>

<project

    name="MultiDemo_"

    default="help">


    <!-- 不需要修改的 开始 -->

    <!-- dex在asset目录中的位置, -->

    <propertyname="dex.asset.dir"value="dex"/>

     <!-- aseent/dex目录中 -->

     <propertyname="mulite.abs.dir"value="${asset.absolute.dir}/${dex.asset.dir}"/>

     <!-- 不需要修改的 结束 -->

     

     

     <!--  拆分dex 1 -->

    <!-- dex在dex.asset.dir文件夹中的文件名称 -->

    <propertyname="dex.file.name1"value="classes1"></property>

    <!-- 保存被拆分出来的class文件的路径-->

    <propertyname="out.multi.classes.absolute.dir1"location="${out.dir}/${dex.file.name1}"/>

    <!-- 编译后,生成的dex文件的绝对路径,包含文件名 -->

    <propertyname="intermediate.dex.file1"location="${out.absolute.dir}/${dex.file.name1}.dex"/>

     

    

      <!--  拆分dex 2 -->

     <propertyname="dex.file.name2"value="classes2"></property>

    <!-- 保存被拆分出来的class文件的路径-->

    <propertyname="out.multi.classes.absolute.dir2"location="${out.dir}/${dex.file.name2}"/>

    <!-- 编译后,生成的dex文件的绝对路径,包含文件名 -->

    <propertyname="intermediate.dex.file2"location="${out.absolute.dir}/${dex.file.name2}.dex"/>

     

    

    <!-- 创建保存拆分class文件的路径,每个dex文件需要一个路径 , 需要修改 -->

   <targetname="-init">

    <!-- 创建第一个需要被拆分的文件路径 -->

    <mkdirdir="${out.multi.classes.absolute.dir1}"></mkdir>

    

     <mkdirdir="${out.multi.classes.absolute.dir2}"></mkdir>

    

    </target>

    

    <!-- 代码混淆后执行移动class文件策略,不需要修改 -->

    <targetname="-post-obfuscate"  depends="-init">

        <!-- 将需要拆分的class文件,移动到相应的路径中 -->

          <antcalltarget="-move-mulit-class"></antcall>

    </target>

    

    <!-- 移动class文件,不需要修改 -->

    <macrodefname="move-class">

        <attributename="srcDir"description="源class做在的相对路径,bin/目录开始"></attribute>

        <attributename="disDir"description="目标class做在的相对路径,bin/目录开始"></attribute>

        <sequential>

              <if>

            <condition>

                <and>

                    <lengthstring="@{srcDir}" trim="true" when="greater"length="0" /> 

                </and>

                <and>

                     <lengthstring="@{disDir}" trim="true" when="greater"length="0" /> 

                </and>

            </condition>

            <then>

                 <movefile="@{srcDir}" todir="@{disDir}" ></move>

            </then>

            <else>

                 <echo> 为设置远文件,或目标文件</echo>

            </else>

            </if>

            

        </sequential>

    </macrodef>

    

      <!-- 移动需要拆分的class文件 ,需要修改-->

    <targetname="-move-mulit-class">

        <!-- 将所有需要拆封在一个dex文件中的class移动一个文件夹中 -->

        <move-classsrcDir="${out.dir}/classes/com/multi/dex1"disDir="${out.multi.classes.absolute.dir1}/com/multi/"></move-class>

        

        <!-- dex 2 中包含的class文件 -->

        <move-classsrcDir="${out.dir}/classes/com/multi/dex2"disDir="${out.multi.classes.absolute.dir2}/com/multi/"></move-class>

    </target>

    

    

    <!-- 主dex编译结束后,编译拆分的dex,需要修改 -->

    <targetname="-post-dex">

             <!--打包第一个被拆分dex文件 -->

             <echo>编译拆分dex 1</echo>

             <dex executable="${dx}"

                    output="${intermediate.dex.file1}"

                    nolocals="@{nolocals}"

                    forceJumbo="${dex.force.jumbo}"

                    disableDexMerger="${dex.disable.merger}"

                    verbose="${verbose}">

                <pathpath="${out.multi.classes.absolute.dir1}"/>

            </dex>

            <!-- 打包第二个被拆分的dex文件 -->

             <echo>编译拆分dex 2</echo>

             <dex executable="${dx}"

                    output="${intermediate.dex.file2}"

                    nolocals="@{nolocals}"

                    forceJumbo="${dex.force.jumbo}"

                    disableDexMerger="${dex.disable.merger}"

                    verbose="${verbose}">

                <pathpath="${out.multi.classes.absolute.dir2}"/>

            </dex>

    </target>

    

    <!-- 将分拆的dex文件,复制到assents/dex目录,需要修改 -->

     <targetname="-pre-crunch">

         <!-- copy dex1 -->

          <echo>复制拆分dex 1</echo>

         <movefile="${intermediate.dex.file1}"todir="${mulite.abs.dir}"></move>

          <!-- copy dex2 -->

           <echo>复制拆分dex 2</echo>

         <movefile="${intermediate.dex.file2}"todir="${mulite.abs.dir}"></move>

     </target>

     

     

     <!-- 编译结束,删除压缩的dex文件, 需要修改-->

     <targetname="-post-build">

          <!-- 删除dex1 -->

         <deletefile="${mulite.abs.dir}/${dex.file.name1}.dex"></delete>

         <!-- 删除dex2 -->

         <deletefile="${mulite.abs.dir}/${dex.file.name2}.dex"></delete>

     </target>

     

  


 

   

</project>


通过项目三步,编译好的apk的asset/dex目录下,就会存在被拆分出来的dex文件。


最后一步,将拆分出来的dex文件添加到主classload中

   方案:

     1.通过反射将当前已经加载进来的dex文件对接保存到内存中:
       getDexElementsByReflect(getPathListByReflect(pathClassLoader));

     2.将指定路径中的dex文件加载到内存中:
     

 /**

     * 加载 指定dex到内存中

     * 

     * @param context

     * @param dexPath

     * @param pathClassLoader

     * @return

     */

    private Object loadDex(Context context, String dexPath, PathClassLoader pathClassLoader) {


        // 加载指定路径的dex文件

        DexClassLoader dexClassLoader =

                new DexClassLoader(dexPath, context.getDir("dex", 0).getAbsolutePath(), dexPath, pathClassLoader);

        // 反射获取DexClasLoader的pathList属性

        Object classLoaderPathList = getPathListByReflect(dexClassLoader);

        if (classLoaderPathList == null) {

            if (is_debug) {

                Log.e(TAG, "路径--》" + dexPath + " 加载到任何的class,请确认文件格式是否正确");

            }

            return null;

        }

        // 反射获取dalvik.system.DexPathList的dexElements熟悉

        return getDexElementsByReflect(classLoaderPathList);

    }

  3.将所有的dex,合并为一个数组,然后通过反射设置给主classloader。这样就完成,代码如下:

    

 // 合并所有的dex文件

        Object array = createObjectTypeArray(allDexElements[0].getClass(), dexNum);


        Object[] list = (Object[]) array;

        dexNum = 0;

        for (int i = 0; i < allDexElements.length; i++) {

            if (allDexElements[i] != null) {

                elementList = (Object[]) allDexElements[i];

                System.arraycopy(elementList, 0, list, dexNum, elementList.length);

                dexNum = dexNum + elementList.length;

            }


        }

        Object pathList = getPathListByReflect(pathClassLoader);

        //

        setFieldByReflect(pathList, pathList.getClass(), "dexElements", list);

       
  参考源码:MultiDex.java


具体可以参考源码:


源码代码 


0 0
原创粉丝点击