彻底掌握Android多分包技术MultiDex-用Ant和Gradle分别构建(一)
Andrid多分包技术在大型项目编译方面起着至关重要的作用,作为一个高级开发者我们有必要掌握此技能,现在我带领大家统一学习此项技能,并教会大家分别使用Ant和Gradle构建。
什么是Dex
Dex是Dalvik VM executes的全称,即Android Dalvik执行程序。在Android中单个Dex文件所能包含的最大方法数为65536,这包含Android FrameWork、依赖的Jar包,以及应用本身的代码中所有的方法。
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构建。
Ant构建MultiDex
Ant是一种基于Java的build工具。理论上来说,它有些类似于(Unix)C中的make ,但没有make的缺陷。
(一)搭建Ant编译环境
1.首先下载Ant:http://ant.apache.org/bindownload.cgi
下载后,我们解压到指定路径,这里我解压到D盘
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"?><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 name="init" > <echo message="init..." /> <delete includeemptydirs="true" > <fileset dir="${bin}" > <include name="**/*" > include> fileset> delete> <mkdir dir="${bin}" /> target> <target name="gen-R" depends="init" > <echo message="Generating R.java from the resources." /> <exec executable="${tools.aapt}" failonerror="true" > <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> <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> <fileset dir="${libs}" includes="*.jar" /> classpath> javac> target> <target name="multi-dex" depends="compile" > <echo message="Generate multi-dex..." /> <exec executable="${tools.dx}" failonerror="true" > <arg value="--dex" /> <arg value="--multi-dex" /> <arg value="--set-max-idx-number=10000" /> <arg value="--main-dex-list" /> <arg value="${main-dex-rule}" /> <arg value="--minimal-main-dex" /> <arg value="--output=${bin}" /> <arg value="${bin}" /> exec> target> <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> <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> <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> <target name="aapt-add-dex" > <echo message="${dir.name}" /> <echo message="执行了app" /> <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> <target name="sign-apk" depends="add-subdex-toapk" > <echo message="Sign apk..." /> <exec executable="${tools.jarsigner}" failonerror="true" > <arg value="-keystore" /> <arg value="${keystore-name}" /> <arg value="-storepass" /> <arg value="123456" /> <arg value="-keypass" /> <arg value="123456" /> <arg value="-signedjar" /> <arg value="${signed-apk-path}" /> <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>
为了方便大家理解,这里我们对build的流程进行分析,详见下图:
main-dex-rule.txt
该文件中只放置了一个class文件
com/castiel/demo/MainActivity.class
ant编译前整个项目结构
ant脚本编译过程
在执行cmd命令,进入项目根目录路径,然后执行ant命令
编译成功后,解压APK可以看到我们成功的实现了多分包技术,生成两个dex文件。
最后成功运行项目
下一篇博客,我们将和大家一起学习使用Gradle构建。
彻底掌握Android多分包技术MultiDex-用Ant和Gradle分别构建(二)
更多相关文章
- android用户输入系统详细说明
- 禁止Eclipse中xml文件Run as的XSL Transformation生成out.xml以
- Android的NDK开发(1)-不一样的HelloWorld
- [Android]用户界面设计
- android用户输入系统
- 跟Google学习Android开发-起始篇-保存数据(1)
- cocos2d-x 2.0.1版本的使用 在android 上运行 初学篇(2)
- 第四章 常见 Android(安卓)文件格式(一)(库文件、APK)
- Android(安卓)Studio如何在项目中使用jni以及OpenCV库