完全掌握Android多分包技術MultiDex-用Ant和Gradle分別構建(一)

    Andrid多分包技術在大型項目編譯方面起着相當重要的做用,做爲一個高級開發者咱們有必要掌握此技能,如今我帶領你們統一學習此項技能,並教會你們分別使用Ant和Gradle構建。java

什麼是Dex

    Dex是Dalvik VM executes的全稱,即Android Dalvik執行程序。在Android中單個Dex文件所能包含的最大方法數爲65536,這包含Android FrameWork、依賴的Jar包,以及應用自己的代碼中全部的方法。android

65536產生的緣由

  • Android系統中,一個Dex文件中存儲方法id用的是short類型數據,因此致使你的dex中方法不能超過65536
  • 在2.3系統以前,虛擬機內存只分配了5M

多分包技術的應用

    一句話爲了解決單個dex包65536方法數限制問題
    針對於65536的問題,咱們在應用層是沒法改變Android系統的結構的,因此咱們沒法將數據類型從short改變爲int或者其餘類型,也就是說一個dex中的方法數不能超過65536是咱們沒法逾越的鴻溝,咱們只能經過優化項目代碼達到減小一個dex中的方法數的目的,可是隨着時間的推移和功能的增長,總有一天仍是會出現方法數超過65536的狀況,所以根據谷歌官方建議,咱們使用多分包技術。
    其實咱們平常使用的大多數軟件都使用到了多分包技術,好比下面就是咱們解壓了一款知名應用的APK包,咱們能夠看到他們使用了多分包技術,APK中包含三個dex文件,分別是classes.dex,classes2.dex,classes3.dex正則表達式

這裏寫圖片描述

本篇博客首先給你們講解使用ant構建。apache

Ant構建MultiDex

Ant是一種基於Java的build工具。理論上來講,它有些相似於(Unix)C中的make ,但沒有make的缺陷。windows

(一)搭建Ant編譯環境

1.首先下載Ant:http://ant.apache.org/bindownload.cgimarkdown

這裏寫圖片描述

下載後,咱們解壓到指定路徑,這裏我解壓到D盤app

這裏寫圖片描述

2.配置NDK環境變量工具

打開個人電腦–屬性–高級–環境變量學習

新建系統變量ANT_HOME
變量名:ANT_HOME
變量值:D:\apache-ant-1.9.7測試

這裏寫圖片描述

選擇「系統變量」中變量名爲「Path」的環境變量,雙擊該變量,把ANT安裝目錄的絕對路徑,添加到Path變量的值中,並使用半角的分號和已有的路徑進行分隔。
變量名:Path
變量值:%ANT_HOME%\bin;

這裏寫圖片描述

完成以上操做後,ANT環境變量配置結束,咱們測試環境變量的配置成功與否。在cmd命令行窗口輸入「ant -version」,輸出如下信息即爲配置正確。如圖:

這裏寫圖片描述

(二)編寫Ant構建腳本

一般咱們的Ant構建文件都放在SDK根目錄下的tools夾下,在裏面咱們找到ant目錄,進去後找到buildxml文件。

這裏寫圖片描述

這裏咱們能夠把這個build.xml文件拷貝到項目目錄中去,而後進行修改。

這裏寫圖片描述

下面是我配置的build.xml源碼

<?xml version="1.0" encoding="UTF-8"?>
<!-- 版權全部,未經贊成請勿轉載!猴子搬來的救兵 http://blog.csdn.net/mynameishuangshuai -->
<!-- project項目標籤 -->
<project  name="MultiDex" default="release" >

    <!-- 項目編譯環境配置 -->

    <property  name="sdk-folder" value="D:\adt-bundle-windows-x86_64-20140702\sdk" />

    <property  name="platform-folder" value="${sdk-folder}\platforms\android-20" />

    <property  name="platform-tools-folder" value="${sdk-folder}\build-tools\android-4.4W" />

    <property  name="jdk-folder" value="C:\Program Files\Java\jdk1.7.0_17" />

    <property  name="android-jar" value="${platform-folder}\android.jar" />

    <property  name="tools.aapt" value="${platform-tools-folder}/aapt.exe" />

    <property  name="tools.javac" value="${jdk-folder}\bin\javac.exe" />

    <property  name="tools.dx" value="${platform-tools-folder}\dx.bat" />

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

    <property  name="tools.jarsigner" value="${jdk-folder}\bin\jarsigner.exe" />

    <!-- 項目輸入目錄配置 -->

    <property  name="project-dir" value="." />

    <property  name="assets" value="${project-dir}\assets" />

    <property  name="res" value="${project-dir}\res" />

    <property  name="src" value="${project-dir}\src" />

    <property  name="libs" value="${project-dir}\libs" />

    <!-- 項目輸出目錄配置 -->

    <property  name="bin" value="${project-dir}\bin" />

    <property  name="gen" value="${project-dir}\gen" />

    <property  name="manifest" value="${project-dir}\AndroidManifest.xml" />
    <!-- 生成文件放置地方 -->

    <property  name="java-file-gen" value="${gen}\com\castiel\demo\*.java" />

    <property  name="java-file-src" value="${src}\com\castiel\demo\*.java" />

    <property  name="main-dex-name" value="${bin}\classes.dex" />

    <property  name="sub-dex-name" value="${bin}\classes2.dex" />

    <property  name="package-temp-name" value="${bin}\${ant.project.name}.arsc" />
    <!-- 未簽名包 -->

    <property  name="unsigned-apk-name" value="${ant.project.name}_unsigned.apk" />

    <property  name="unsigned-apk-path" value="${bin}\${unsigned-apk-name}" />
    <!-- 簽名包 -->

    <property  name="signed-apk-name" value="${ant.project.name}.apk" />

    <property  name="signed-apk-path" value="${bin}\${signed-apk-name}" />
    <!-- 密鑰 -->

    <property  name="keystore-name" value="${project-dir}\castiel_key.keystore" />

    <property  name="keystore-alias" value="castiel" />

    <property  name="main-dex-rule" value="${project-dir}\main-dex-rule.txt" />

    <taskdef resource="net/sf/antcontrib/antlib.xml" />

    <!-- 初始化target -->

    <target name="init" >

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

        <delete includeemptydirs="true" >

            <fileset dir="${bin}" >

                <include name="**/*" >
                </include>
            </fileset>
        </delete>

        <mkdir dir="${bin}" />
    </target>

    <!-- 生成R.java類文件 -->

    <target  name="gen-R" depends="init" >

        <echo message="Generating R.java from the resources." />

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

            <!-- package表示打包 -->

            <arg value="package" />

            <arg value="-f" />

            <arg value="-m" />

            <arg value="-J" />

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

            <arg value="-S" />

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

            <arg value="-M" />

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

            <arg value="-I" />

            <arg value="${android-jar}" />
        </exec>
    </target>

    <!-- 編譯源文件生成對應的class文件 -->

    <target  name="compile" depends="gen-R" >

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

        <javac  bootclasspath="${android-jar}" compiler="javac1.7" destdir="${bin}" encoding="utf-8" includeantruntime="false" listfiles="true" target="1.7" >

            <src path="${project-dir}" />

            <classpath>

                <!-- 引入第三方jar包所須要引用,用於輔助編譯,並無將jar打包進去。 -->

                <fileset  dir="${libs}" includes="*.jar" />
            </classpath>
        </javac>
    </target>

    <!-- 構建多分包dex文件 -->

    <target  name="multi-dex" depends="compile" >

        <echo message="Generate multi-dex..." />

        <exec  executable="${tools.dx}" failonerror="true" >
            <arg value="--dex" />
            <arg value="--multi-dex" />
            <!-- 多分包命令,每一個包最大的方法數爲10000 -->
            <arg value="--set-max-idx-number=10000" />
            <arg value="--main-dex-list" />
            <!-- 主包包含class文件列表 -->
            <arg value="${main-dex-rule}" />
            <arg value="--minimal-main-dex" />
            <arg value="--output=${bin}" />
            <!-- 把bin下全部class打包 -->
            <arg value="${bin}" />
            <!-- 把libs下全部jar打包 -->
            <!-- <arg value="${libs}" /> -->
        </exec>
    </target>

    <!-- 打包資源文件(包括res、assets、AndroidManifest.xml) -->

    <target  name="package" depends="multi-dex" >

        <echo message="package-res-and-assets..." />

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

            <arg value="package" />

            <arg value="-f" />

            <arg value="-S" />

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

            <arg value="-A" />

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

            <arg value="-M" />

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

            <arg value="-I" />

            <arg value="${android-jar}" />

            <arg value="-F" />
            <!-- 放到臨時目錄中 -->

            <arg value="${package-temp-name}" />
        </exec>
    </target>
    <!-- 對臨時目錄進行打包 -->

    <target  name="build-unsigned-apk" depends="package" >

        <echo message="Build-unsigned-apk" />

        <java  classname="com.android.sdklib.build.ApkBuilderMain" classpath="${sdk-folder}/tools/lib/sdklib.jar" >

            <!-- 輸出路徑 -->

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

            <arg value="-u" />

            <arg value="-z" />

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

            <arg value="-f" />

            <arg value="${main-dex-name}" />

            <arg value="-rf" />

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

            <arg value="-rj" />

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

    <!-- 拷貝文件到apk項目的根目錄下 -->

    <target  name="copy_dex" depends="build-unsigned-apk" >

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

        <copy todir="${project-dir}" >

            <fileset dir="${bin}" >

                <include name="classes*.dex" />
            </fileset>
        </copy>
    </target>

    <!-- 循環遍歷bin目錄下的全部dex文件 -->
    <target  name="add-subdex-toapk" depends="copy_dex" >

        <echo message="Add subdex to apk..." />

        <foreach  param="dir.name" target="aapt-add-dex" >

            <path>

                <fileset  dir="${bin}" includes="classes*.dex" />
            </path>
        </foreach>
    </target>

    <!-- 使用aapt命令添加dex文件 -->

    <target name="aapt-add-dex" >
        <echo message="${dir.name}" />
        <echo message="執行了app" />
        <!-- 使用正則表達式獲取classes的文件名 -->
        <propertyregex  casesensitive="false" input="${dir.name}" property="dexfile" regexp="classes(.*).dex" select="\0" />
        <if>
            <equals  arg1="${dexfile}" arg2="classes.dex" />
            <then>
                <echo>
                   ${dexfile} is not handle
                </echo>
            </then>
            <else>
                <echo>
                    ${dexfile} is handle
                </echo>
                <exec  executable="${tools.aapt}" failonerror="true" >
                    <arg value="add" />
                    <arg value="${unsigned-apk-path}" />
                    <arg value="${dexfile}" />
                </exec>
            </else>
        </if>
        <delete file="${project-dir}\${dexfile}" />
    </target>

    <!-- 生成簽名的apk -->
    <target  name="sign-apk" depends="add-subdex-toapk" >

        <echo message="Sign apk..." />

        <exec  executable="${tools.jarsigner}" failonerror="true" >
            <!-- keystore -->
            <arg value="-keystore" />
            <arg value="${keystore-name}" />
            <!-- 祕鑰 -->
            <arg value="-storepass" />
            <arg value="123456" />
            <!-- 祕鑰口令 -->
            <arg value="-keypass" />
            <arg value="123456" />
            <arg value="-signedjar" />
            <!-- 簽名的apk -->
            <arg value="${signed-apk-path}" />
            <!-- 未簽名的apk -->
            <arg value="${unsigned-apk-path}" />
            <!-- 別名 -->
            <arg value="${keystore-alias}" />
        </exec>
    </target>

    <!-- 簽名發佈 -->

    <target  name="release" depends="sign-apk" >

        <delete file="${package-temp-name}" />

        <delete file="${unsigned-apk-path}" />

        <echo>
APK is released.path:${signed-apk-path}

        </echo>
    </target>

</project> 

<!-- 版權全部,未經贊成請勿轉載!猴子搬來的救兵 http://blog.csdn.net/mynameishuangshuai -->

爲了方便你們理解,這裏咱們對build的流程進行分析,詳見下圖:

這裏寫圖片描述

main-dex-rule.txt
該文件中只放置了一個class文件

com/castiel/demo/MainActivity.class

ant編譯前整個項目結構

這裏寫圖片描述

ant腳本編譯過程

在執行cmd命令,進入項目根目錄路徑,而後執行ant命令

這裏寫圖片描述

這裏寫圖片描述

編譯成功後,解壓APK能夠看到咱們成功的實現了多分包技術,生成兩個dex文件。

這裏寫圖片描述

最後成功運行項目

這裏寫圖片描述

下一篇博客,咱們將和你們一塊兒學習使用Gradle構建。

完全掌握Android多分包技術MultiDex-用Ant和Gradle分別構建(二)

相關文章
相關標籤/搜索