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構建。