最近在研究Android 的Ant 编译环境,遂将经验总结写下来。大家都知道我们可以在eclipse 里面编译android 的工程,这是怎么完成的呢?它主要是由于ADT 插件调用android的build tools完成的。其实eclipse 的编译也是通过Ant 脚本来完成编译的。/adnroid-sdk/tools/ant/build.xml 这就是ADT 插件调用的ant脚本,通过执行这个脚本就可以在完成android工程的编译。
那么如果不依赖ADT 插件,我们可以完成android 工程的编译呢?当然可以,首先必须安装Apache Ant,目前最新版本是1.9.4,下载解压放到android sdk 目录下,然后将其bin路径添加到环境变量PATH 。最后只需要在你的android 工程目录下编写build.xml 文件。
1. 如何编译Jar 文件,啥也不说了,直接上代码
下面为编译脚本build.sh 文件,最终会调用build.xml 文件。local.properties 文件是Ant 自动生成的,里面会自动指定 sdk.dir 变量,另外我们还手动指定了一个me.version,指定编译的版本号。
#!/bin/bash
PROP_FILE=local.properties
PROP_VERSION_KEY=me.version
VERSION=`cat ${PROP_FILE} | grep ${PROP_VERSION_KEY} | cut -d'=' -f2`
GITSHA1=`git log | sed -n '1p' | awk '{print $2}'`
MTL_OUTPUT=target
echo '
###################################################
#
# Android Build system
#
###################################################
'
#预设置android sdk目录
android update project -p .
#1.编译jni
ndk-build clean
ndk-build
#2.编译jar
ant -f build.xml
echo '
#################################################
#
# Make the tar package
#
#################################################
'
tar -cvf ${MTL_OUTPUT}/aliroot_${VERSION}_${GITSHA1}.tar.gz sdk
local.properties 文件:
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must *NOT* be checked into Version Control Systems,
# as it contains information specific to your local configuration.
# location of the SDK. This is only used by Ant
# For customization when using a Version Control System, please read the
# header note.
sdk.dir=/Users/mars/development/AndroidSDK/sdk
me.version=1.1.0
build.xml 文件:
<project default="jar" name="Acid" basedir=".">
<!-- 工程目录结构
project
|-bin
|-src
|-lib
|-out
|-build.xml
-->
<property file="local.properties" />
<!-- ANT环境变量 -->
<property environment="env" />
<!-- SDK目录(获取操作系统环境变量ANDROID_SDK_HOME的值) -->
<!-- <property name="sdk-folder" value="${env.ANDROID_HOME}" /> -->
<!-- property name="sdk-folder" value="${env.ANDROID_SDK_HOME}" /> -->
<property name="sdk-folder" value="${sdk.dir}" />
<!-- SDK指定平台中build tools目录 -->
<property name="sdk-build-tools" value="${sdk-folder}/build-tools/19.0.0" />
<property name="sdk-tools" value="${sdk-folder}/tools" />
<!-- SDK指定平台中platforms目录 -->
<property name="sdk-platform-folder" value="${sdk-folder}/platforms/android-19" />
<!-- SDK指定平台android.jar目录 -->
<property name="android-jar" value="${sdk-platform-folder}/android.jar" />
<!-- 编译aidl文件所需的预处理框架文件framework.aidl -->
<property name="framework-aidl" value="${sdk-platform-folder}/framework.aidl" />
<!-- AIDL build tool -->
<condition property="exe" value=".exe" else=""><os family="windows" /></condition>
<condition property="bat" value=".bat" else=""><os family="windows" /></condition>
<property name="aidl" value="${sdk-build-tools}/aidl${exe}" />
<property name="proguard.jar" location="${sdk-tools}/proguard/lib/proguard.jar" />
<loadproperties srcFile="project.properties" />
<condition property="proguard.enabled" value="true">
<isset property="proguard.config" />
</condition>
<property name="libs.dir" value="libs"/>
<property name="src.dir" value="src/com/alibaba/alibaba_root"/>
<property name="aidl.dir" value="src"/>
<property name="classes.obj.dir" value="classes"/>
<property name="classes.obj.bin.dir" value="classes/bin"/>
<!-- 生成R文件的相对目录 -->
<property name="outgen.dir" value="gen" />
<property name="output.dir" value="sdk"/>
<property name="jarname" value="alibaba_root_${me.version}.jar"/>
<property name="obfuscate.jarname" value="alibaba_root_obfuscate_${me.version}.jar"/>
<property name="mainclass" value="com.alibaba.alibaba_root"/>
<property name="manifest.file" value="AndroidManifest.xml"/>
<!-- 第三方jar包的路径 -->
<path id="lib-classpath">
<!--<fileset dir="${libs.dir}">-->
<fileset dir="libs">
<include name="**/*.jar"/>
<!-- <include name="android.jar"/>-->
</fileset>
</path>
<!-- 1.1 初始化工作,如创建目录等 -->
<target name="init">
<delete dir="${classes.obj.dir}" />
<delete dir="${classes.obj.bin.dir}" />
<mkdir dir="${classes.obj.dir}"/>
<mkdir dir="${classes.obj.bin.dir}"/>
<mkdir dir="${outgen.dir}"/>
<mkdir dir="${output.dir}"/>
<mkdir dir="${output.dir}/armeabi"/>
<mkdir dir="${output.dir}/assets"/>
<taskdef
name="xmltask"
classname="com.oopsconsultancy.xmltask.ant.XmlTask"
classpath="./forbuild/xmltask.jar" />
<xmltask
dest="${basedir}/${manifest.file}"
encoding="utf-8"
source="${basedir}/${manifest.file}" >
<!-- 设置isdebug=true -->
<!-- 运行环境,线上0,日常1,预发2 -->
<attr
attr="android:value"
path="//manifest/application/meta-data[@android:name='root_server_config']"
value="0" />
</xmltask>
</target>
<!--1.2 编译aidl文件 -->
<target name="aidl" depends="init">
<echo>Compiling .aidl into java files...</echo>
<apply executable="${aidl}" failοnerrοr="true">
<!-- 指定预处理文件 -->
<arg value="-p${framework-aidl}" />
<!-- aidl声明的目录 -->
<arg value="-I${aidl.dir}" />
<!-- 目标文件目录 -->
<arg value="-o${outgen.dir}" />
<!-- 指定哪些文件需要编译 -->
<fileset dir="${aidl.dir}">
<include name="**/*.aidl" />
</fileset>
</apply>
</target>
<!-- 2. 编译整个sdk -->
<target name="compile" depends="aidl">
<echo>Compiling java source code...</echo>
<javac destdir="${classes.obj.dir}" bootclasspath="${android-jar}">
<src path="${src.dir}" />
<src path="${outgen.dir}" />
<compilerarg line="-encoding UTF-8"/>
<classpath refid="lib-classpath"/>
</javac>
</target>
<!-- 3. 打包jar文件 -->
<target name="jar" depends="compile">
<copy todir="${output.dir}/armeabi">
<fileset dir="${libs.dir}/armeabi"/>
</copy>
<copy todir="${output.dir}/assets">
<fileset dir="assets"/>
</copy>
<!--Create a property containing all .jar files,
prefix lib/, and seperated with a space-->
<pathconvert property="mf.classpath" pathsep=" ">
<mapper>
<chainedmapper>
<!-- jar包文件只留文件名,去掉目录信息 -->
<flattenmapper/>
<!-- add lib/ prefix -->
<globmapper from="*" to="lib/*"/>
</chainedmapper>
</mapper>
<path refid="lib-classpath"/>
</pathconvert>
<!-- jar文件的输出路径 -->
<jar destfile="${classes.obj.bin.dir}/${jarname}" basedir="${classes.obj.dir}">
<manifest>
<attribute name="Main-class" value="${mainclass}"/>
<attribute name="Class-Path" value="${mf.classpath}"/>
</manifest>
</jar>
<antcall target="obfuscate" />
</target>
<!-- proguard 混淆 -->
<target name="obfuscate" if="proguard.enabled">
<echo>Start proguard obfuscate...</echo>
<taskdef resource="proguard/ant/task.properties" classpath="${proguard.jar}" />
<proguard configuration="${basedir}/proguard-project.txt">
-injar ${classes.obj.bin.dir}/${jarname}
-outjar ${output.dir}/${obfuscate.jarname}
-libraryjar ${android-jar}
-libraryjar ${libs.dir}/securityguard-2.3.39.jar
-libraryjar ${libs.dir}/umid-1.3.0.jar
</proguard>
<!-- move file="${output.dir}/${obfuscate.jarname}" tofile="${output.dir}/${jarname}"/-->
</target>
</project>
proguard-project.txt 文件:
-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontpreverify
#-dontwarn
-verbose
-optimizations !code/simplification/arithmetic,!class/merging/*
-keepattributes InnerClasses,Exceptions
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class com.android.vending.licensing.ILicensingService
-keepclasseswithmembernames class * {
native <methods>;
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet);
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
}
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
-keep interface com.xxx.zzz{public *;}
-keep class com.yyy.JNIHelper
-keepclassmembers class com.yyy.JNIHelper{public *;}
还需要在project.properties 文件中指定proguard.config. 下面是project.properties 文件:
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must be checked in Version Control Systems.
#
# To customize properties used by the Ant build system edit
# "ant.properties", and override values to adapt the script to your
# project structure.
#
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
# Project target.
target=android-19
android.library=false
proguard.config=proguard-project.txt
2. 如何编译APK文件:build.sh,local.properties 文件都跟上面一样。只需要修改build.xml 文件:
<?xml version="1.0" encoding="UTF-8"?>
<project name="RootDemoApk_Offline" default="release" basedir=".">
<property file="local.properties" />
<property environment="env" />
<!-- SDK Locations -->
<!-- SDK目录(获取操作系统环境变量ANDROID_SDK_HOME的值) -->
<!-- <property name="sdk-folder" value="${env.ANDROID_HOME}" /> -->
<!-- <property name="sdk-folder" value="${env.ANDROID_SDK_HOME}" /> -->
<property name="sdk-folder" value="${sdk.dir}" />
<property name="sdk-platform" value="${sdk-folder}/platforms/android-19" />
<property name="sdk-tools" value="${sdk-folder}/tools" />
<property name="sdk-build-tools" value="${sdk-folder}/build-tools/19.0.0" />
<property name="sdk-platform-tools" value="${sdk-folder}/platform-tools" />
<property name="proguardpath" location="${wtkhome}/lib/proguard" />
<!-- step 1.generate R.java by aapt 2.compile java to class by javac 3.generate classes.dex by dx.bat -->
<!-- 4.package resources by aapt 5. package resource and classes.dex by apkbuilder.bat 6.sign apk by jarsinger -->
<!-- Tools -->
<property name="aapt" value="${sdk-build-tools}/aapt" />
<property name="dx" value="${sdk-build-tools}/dx" />
<property name="aidl" value="${sdk-build-tools}/aidl" />
<property name="adb" value="${sdk-platform-tools}/adb" />
<property name="android-jar" value="${sdk-platform}/android.jar" />
<property name="jarsigner" value="${env.JAVA_HOME}/bin/jarsigner" />
<!-- Application Package Name -->
<property name="application-package" value="com.alibaba.alibaba_root_demo" />
<property name="version" value="1.0.0" />
<property name="debug.keystore" value="${basedir}/debug.keystore" />
<property name="manifest.file" value="AndroidManifest.xml"/>
<!-- The intermediates directory -->
<!-- Eclipse uses "bin" for its own output, so we do the same. -->
<property name="outdir-bin" value="target" />
<property name="outdir-gen" value="gen" />
<!-- source directories -->
<property name="resource-dir" value="res" />
<property name="asset-dir" value="assets" />
<property name="srcdir" value="src" />
<property name="srcdir-absolutepath" value="${basedir}/${srcdir}" />
<property name="external-libs" value="libs" />
<property name="external-libs-absolutepath" value="${basedir}/${external-libs}" />
<!-- dest directories -->
<property name="des-resource-dir" value="${outdir-bin}/res" />
<property name="des-asset-dir" value="${outdir-bin}/assets" />
<property name="des-srcdir" value="${outdir-bin}/src" />
<property name="des-external-libs" value="${outdir-bin}/libs" />
<!-- Output directories -->
<property name="outdir-classes" value="${outdir-bin}/src" />
<!-- Intermediate files -->
<property name="dex-file" value="classes.dex" />
<property name="intermediate-dex" value="${outdir-bin}/${dex-file}" />
<property name="intermediate-dex-absolutepath" value="${basedir}/${intermediate-dex}" />
<!-- The final package file to generate -->
<property name="resources-package" value="${outdir-bin}/resource.ap_" />
<property name="resources-package-absolutepath" value="${basedir}/${resources-package}" />
<property name="out-unsigned-package" value="${outdir-bin}/${ant.project.name}-unsigned.apk" />
<property name="out-unsigned-package-absolutepath" value="${basedir}/${out-unsigned-package}" />
<property name="out-signed-package" value="${outdir-bin}/${ant.project.name}.apk" />
<property name="out-signed-package-absolutepath" value="${basedir}/${out-signed-package}" />
<!-- init -->
<target name="init">
<echo>Creating all output directories </echo>
<delete dir="${outdir-bin}" />
<delete dir="${outdir-gen}" />
<mkdir dir="${outdir-bin}" />
<mkdir dir="${outdir-classes}" />
<mkdir dir="${outdir-gen}" />
<taskdef name="xmltask" classname="com.oopsconsultancy.xmltask.ant.XmlTask" classpath="../forbuild/xmltask.jar" />
<xmltask
dest="${basedir}/${manifest.file}"
encoding="utf-8"
source="${basedir}/${manifest.file}" >
<!-- 设置isdebug=true -->
<!-- 运行环境,线上0,线下1 -->
<attr attr="android:value"
path="//manifest/application/meta-data[@android:name='root_server_config']"
value="1" />
</xmltask>
</target>
<!-- Generate the R.java file for this project's resources. -->
<target name="resource-src" depends="init">
<echo>Generating R.java / Manifest.java from the resources...</echo>
<exec executable="${aapt}" failοnerrοr="true">
<arg value="package" />
<arg value="-m" />
<arg value="-J" />
<arg value="${outdir-gen}" />
<arg value="-M" />
<arg value="AndroidManifest.xml" />
<arg value="-S" />
<arg value="${resource-dir}" />
<arg value="-I" />
<arg value="${android-jar}" />
</exec>
</target>
<!-- Generate java classes from .aidl files. -->
<target name="aidl" depends="resource-src">
<echo>Compiling aidl files into Java classes...</echo>
<apply executable="${aidl}" failοnerrοr="true">
<arg value="-p${android-framework}" />
<arg value="-I${srcdir}" />
<fileset dir="${srcdir}">
<include name="**/*.aidl" />
</fileset>
</apply>
</target>
<!-- Compile this project's .java files into .class files. -->
<target name="compile" depends="aidl">
<echo>Compiling java source code...</echo>
<javac destdir="${outdir-classes}" bootclasspath="${android-jar}">
<src path="${srcdir}" />
<src path="${outdir-gen}"/>
<compilerarg line="-encoding UTF-8"/>
<classpath>
<fileset dir="${external-libs}" includes="*.jar" />
</classpath>
</javac>
</target>
<!-- Convert this project's .class files into .dex files. -->
<target name="dex" depends="compile">
<echo>Converting compiled files and external libraries into ${outdir-bin}/${dex-file}...</echo>
<apply executable="${dx}" failοnerrοr="true" parallel="true">
<arg value="--dex" />
<arg value="--output=${intermediate-dex-absolutepath}" />
<arg path="${outdir-classes}" />
<fileset dir="${external-libs}" includes="*.jar" />
</apply>
</target>
<!-- Put the project's resources into the output package file. -->
<target name="package-res-and-assets" depends="dex">
<echo>Packaging resources and assets...</echo>
<exec executable="${aapt}" failοnerrοr="true">
<arg value="package" />
<arg value="-f" />
<arg value="-M" />
<arg value="AndroidManifest.xml" />
<arg value="-S" />
<arg value="${resource-dir}" />
<arg value="-A" />
<arg value="${asset-dir}" />
<arg value="-I" />
<arg value="${android-jar}" />
<arg value="-F" />
<arg value="${resources-package}" />
</exec>
</target>
<!-- Same as package-res-and-assets, but without "-A ${asset-dir}" -->
<!--
<target name="package-res-no-assets">
<echo>Packaging resources...</echo>
<exec executable="${aapt}" failοnerrοr="true">
<arg value="package" />
<arg value="-f" />
<arg value="-M" />
<arg value="AndroidManifest.xml" />
<arg value="-S" />
<arg value="${resource-dir}" />
<arg value="-I" />
<arg value="${android-jar}" />
<arg value="-F" />
<arg value="${resources-package}" />
</exec>
</target>
-->
<!-- apk-builder 在android 高版本上没有,它是一个打包脚本,相当于下面 -->
<target name="package" depends="package-res-and-assets">
<java classpath="${sdk-tools}/lib/sdklib.jar" classname="com.android.sdklib.build.ApkBuilderMain">
<arg value="${out-unsigned-package-absolutepath}" />
<arg value="-u" />
<arg value="-z" />
<arg value="${resources-package-absolutepath}" />
<arg value="-f" />
<arg value="${intermediate-dex-absolutepath}" />
<arg value="-rf" />
<arg value="${srcdir-absolutepath}" />
<arg value="-rj"/>
<arg value="${external-libs-absolutepath}"/>
<!-- libs下面的so库打包 -->
<arg value="-nf" />
<arg value="${external-libs-absolutepath}" />
</java>
</target>
<target name="jarsigner" depends="package">
<echo>jarsigner ${out-unsigned-package} for release...</echo>
<exec executable="${jarsigner}" failοnerrοr="true">
<arg value="-verbose" />
<arg value="-storepass" />
<arg value="android" />
<arg value="-keypass" />
<arg value="android" />
<arg value="-keystore" />
<arg value="${debug.keystore}" />
<arg value="-signedjar" />
<arg value="${out-signed-package-absolutepath}" />
<arg value="${out-unsigned-package-absolutepath}" />
<arg value="androiddebugkey" />
</exec>
</target>
<target name="release" depends="jarsigner">
<echo>jarsigner for release succefully...</echo>
</target>
<!-- Install the package on the default emulator -->
<!--
<target name="install" depends="debug">
<echo>Installing ${out-debug-package} onto default emulator...</echo>
<exec executable="${adb}" failοnerrοr="true">
<arg value="install" />
<arg value="${out-debug-package}" />
</exec>
</target>
<target name="reinstall" depends="debug">
<echo>Installing ${out-debug-package} onto default emulator...</echo>
<exec executable="${adb}" failοnerrοr="true">
<arg value="install" />
<arg value="-r" />
<arg value="${out-debug-package}" />
</exec>
</target>
-->
<!-- Uinstall the package from the default emulator -->
<!--
<target name="uninstall">
<echo>Uninstalling ${application-package} from the default emulator...</echo>
<exec executable="${adb}" failοnerrοr="true">
<arg value="uninstall" />
<arg value="${application-package}" />
</exec>
</target>
-->
</project>