雖然當下Android Studio盛行,昨天有朋友問到了用Ant 怎么自動打包,現在自動打包確實仍然是部分像我這樣的菜鳥面臨的問題,既然有人問就借此契機搞一波,說不定什么時候自己就用上了。
需求是要提供方便的方式在打包的時候修改 package、logo、label、meta-data(包名、圖標、名稱、第三方SDK配置),以及個別Java類的字段。
原料:ADT、Android SDK、apache-ant-1.9.7、xmltask.jar。
這篇博客就是我昨天求知的坎坷過程。
首先要想使用Ant打包項目,第一步肯定是要配置相關的環境,默認創建的Android項目是沒有Ant環境的,關於Ant我很久很久之前也寫過一點東西或多或少能幫助理解什么是Ant,要想查看請點擊這里,
要配置Ant環境其實並不難,百度一波就能看到很多,這里我就把我配置的過程記錄下來至於別的方式我就不過多列舉了:
1、先去官網(http://ant.apache.org/bindownload.cgi)下載 apache-ant-1.9.7-bin.zip ,解壓安裝
2、配置環境變量以便於在任何目錄都能使用命令行執行命令,以我的配置為例:
添加系統變量 ANT_HOME D:\Program Files\apache-ant-1.9.7-bin\apache-ant-1.9.7
在Path中添加 ;%ANT_HOME%\bin
測試配置是否成功,在命令行輸入ant -version
至此Ant已經安裝成功,環境變量也配置好了。
3、為Android項目配置Ant環境,這里要使用到SDK中android.bat 批處理文件來配置工程里build.xml腳本,以及相關文件,具體操作如下:
a.為了演示我創建Android項目(AntPackage)
進入命令行切換到SDK/tool目錄下,(或者在sdk\tools目錄下直接進入命令行)
輸入android -h可看到如下幫助提示:
這些都是該批處理文件給我們提供的命令,我們要用到的是 android update project ,
android update project 可以有一下參數:
以上命令仔細看看都是我們可以理解的一些命令。
我在這里使用的命令如下: android update project -n AntPackage -t 2 -p D:\WorkSpace\Eclipse-Android2\AntPackage
刷新我們的項目可以看到SDK為該項目配置的 build.xml、loacal.properties、以及其他的相關文件,
4、添加 ant.properties、custom_rules.xml
打開build.xml里面的一些命令或者注釋仔細看我們都是可以看懂的,其中有一段注釋是
<!-- The ant.properties file can be created by you. It is only edited by the 'android' tool to add properties to it. This is the place to change some Ant specific build properties. Here are some properties you may want to change/update: source.dir The name of the source directory. Default is 'src'. out.dir The name of the output directory. Default is 'bin'. For other overridable properties, look at the beginning of the rules files in the SDK, at tools/ant/build.xml Properties related to the SDK location or the project target should be updated using the 'android' tool with the 'update' action. This file is an integral part of the build system for your application and should be checked into Version Control Systems. -->
大致意思就是我們應該創建自己的 ant.properties 我們可以在該文件中定義自己的配置屬性,android tool會讀取這些屬性,那我么就創建這個文件,根據我們的需要添加配置屬性值
#在此文件中定義我們使用到的屬性,中文的話需要轉成UTF-8編碼,可以使用JDK自帶的native2ascii工具轉碼
#包名
application.package=com.tai.autopackage
#項目名,缺省時源碼文件名
ant.project.name=Autopackage
#編碼方式
#java.encoding=utf-8
#編譯輸出絕對路徑
out.absolute.dir=d:/WorkSpace/out
#生成文件絕對路徑
gos.path=d:/WorkSpace/out
#簽名key文件絕對路徑
key.store=D:/WorkSpace/a.keystore
#簽名文件密碼
key.store.password=123456
#簽名別稱
key.alias=auto1
#簽名別稱密碼
key.alias.password=123456
basedir=D:/WorkSpace/Eclipse-Android2/Autopackage
xmltasklib.dir=libs
manifest.file=AndroidManifest.xml
project.name=NewProjectName
android.icon=@drawable/ic_launchernew
android.app_name=dsadasxc
另外還有一段注釋如下:
<!-- Import per project custom build rules if present at the root of the project. This is the place to put custom intermediary targets such as: -pre-build -pre-compile -post-compile (This is typically used for code obfuscation. Compiled code location: ${out.classes.absolute.dir} If this is not done in place, override ${out.dex.input.absolute.dir}) -post-package -post-build -pre-clean -->
這段注釋大致是說我們可以添加自己的build rules文件(custom_rules.xml)來自定義自己的"過渡"target比如-pre-build 等,這些target會執行的原因可以查看sdk/tool/ant/build.xml,里面有如下幾句
<!-- empty default pre-compile target. Create a similar target in your build.xml and it'll be called instead of this one. --> <target name="-pre-compile"/> <!-- Compiles this project's .java files into .class files. --> <target name="-compile" depends="-pre-build, -build-setup, -code-gen, -pre-compile"> <do-only-if-manifest-hasCode elseText="hasCode = false. Skipping..."> <!-- merge the project's own classpath and the tested project's classpath --> <path id="project.javac.classpath"> <path refid="project.all.jars.path" /> <path refid="tested.project.classpath" /> <path path="${java.compiler.classpath}" /> </path> <javac encoding="${java.encoding}" source="${java.source}" target="${java.target}" debug="true" extdirs="" includeantruntime="false" destdir="${out.classes.absolute.dir}" bootclasspathref="project.target.class.path" verbose="${verbose}" classpathref="project.javac.classpath" fork="${need.javac.fork}"> <src path="${source.absolute.dir}" /> <src path="${gen.absolute.dir}" /> <compilerarg line="${java.compilerargs}" /> </javac>
從紅色字體可以看出 sdk build.xml有<target name="-compile" depends="-pre-build, -build-setup, -code-gen, -pre-compile">
這就解釋了我們寫的腳本為什么會執行。
說了這么多貼出來我的custom_rules.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project name="custom_rules"> 3 <path id="xmltask.classpath"> 4 <pathelement path="${basedir}/${xmltasklib.dir}/xmltask.jar" /> 5 </path> 6 7 <taskdef name="xmltask" classname="com.oopsconsultancy.xmltask.ant.XmlTask" classpathref="xmltask.classpath"/> 8 <target name="-pre-build" depends="changeLogo"> 9 <echo>do something others</echo> 10 </target> 11 12 <target name="changeLogo" depends="changeName"> 13 <echo>changelogo...</echo> 14 <xmltask source="${basedir}/${manifest.file}" dest="${basedir}/${manifest.file}" encoding="utf-8" > 15 <attr path="//manifest/application" attr="android:icon" value="${android.icon}" /> 16 </xmltask> 17 </target> 18 19 <target name="changeName" > 20 <echo>changeName...</echo> 21 <xmltask source="${basedir}/res/values/strings.xml" dest="${basedir}/res/values/strings.xml" encoding="utf-8" > 22 <replace path="/resources/string[@name='app_name']/text()" withText="${android.app_name}"/> 23 </xmltask> 24 </target> 25 </project>
看着代碼是不多,但是里面有一個xmltask, xmltask是什么呢?回到我們需求,我們需求是在打包的時候修改 package、logo、label、meta-data(包名、圖標、名稱、第三方SDK配置),以及個別Java類的字段。
Ant可以是提供了命令讓我們修改文件、替換文件,可是我還是覺得不是很方便,為什么呢?
一下是我第一次修改應用名字的方式
<!--<replace file ="res/values/strings.xml" token="Autopackage" value="新的名字"/>-->
這種文本替換的方式從某種意義上說能達到我們需求但是,操作之后文件就會被修改,下次我要打包成別的名字又要修改腳本或者string.xml文件,相當麻煩。同樣的道理用replace命令修改logo也存在一樣的問題。
這種麻煩產生的原因是以上的操作只是針對文本進行的操作,可能ant還有別的好用的命令,但是我們的這些都是都是存在XML文件中的,要是ant能提供一種讓我們直接操作XML的命令那就方便多了。
Ant本身很強大,正因為它的強大才會有很多第三方的擴展包。xmltask就是一個非常方便的操作xml文件的擴展。
要想使用這個xmltask,首先要先下載他們的jar包放到項目里,最重要的是我們要在ant中引入xmltask.一下就是引入方式
<path id="xmltask.classpath"> <pathelement path="${basedir}/${xmltasklib.dir}/xmltask.jar" /> </path> <taskdef name="xmltask" classname="com.oopsconsultancy.xmltask.ant.XmlTask" classpathref="xmltask.classpath"/>
里面的 basedir 、 xmltasklib.dir以及別的屬性都是我們在ant.properties中定義好的。
OK,到此一切都配置妥當,讓我們來試着打包一次,從Android項目根目錄進入命令行執行 如下:
... ... ....
OK到這里我們就完成了動態的在打包的時候完成修改logo|name的操作,再看看怎么去修改應用中的某些Java變量
現在我們有個常量Java類,里面有我們應用請求的根目錄和請求的跟地址如下:
1 package com.tai.antpackage.constants; 2 3 public class HttpConstants { 4 5 public final static String URL = "${URL}"; 6 public final static String BASEROOT = "${BASEROOT}"; 7 /** 8 * other variables and methods 9 */ 10 }
這里我們首先把 URL 、BASEROOT 的都寫的比較特殊,目的是為了在做替換的時候防止替換到別的變量或者文本。
一下是我們替換值得命令:
<target name="changeName" depends="changeJavaVariable"> <echo>changeName...</echo> <xmltask source="${basedir}/res/values/strings.xml" dest="${basedir}/res/values/strings.xml" encoding="utf-8" > <replace path="/resources/string[@name='app_name']/text()" withText="${android.app_name}"/> </xmltask> </target> <target name="changeJavaVariable"> <echo>change java file variable value...</echo> <!--copy file="${basedir}/${srcdir}/${file.replace.path}/${fileName}" todir="res" /--> <replace file="${basedir}/${srcdir}/${file.replace.path}/${fileName}" token="${UrlKey}" value="${UrlValue}" encoding="utf-8"/> <replace file="${basedir}/${srcdir}/${file.replace.path}/${fileName}" token="${BaseRootKey}" value="${BaseRootVaue}" encoding="utf-8"/> <delete dir="${basedir}/bin/classes" /> </target>
綠色部分相當於調用語句。與我們上面修改應用名字關聯,修改應用名字之前就把變量修改了。需要注意的是:為了保持我們的代碼不變,我們需要在打包完畢之后還原回變量
目的是為了下次再執行打包命令的時候不用去修改我們的代碼命令如下:(注意該操作是在打包之后執行)
1 <target name="-post-compile" depends="restoreJavaVariable"> 2 <echo>restore java file variable value...</echo> 3 </target> 4 5 <target name="restoreJavaVariable" > 6 <replace file="${basedir}/${srcdir}/${file.replace.path}/${fileName}" token="${UrlValue}" value="${UrlKey}" encoding="utf-8"/> 7 <replace file="${basedir}/${srcdir}/${file.replace.path}/${fileName}" token="${BaseRootVaue}" value="${BaseRootKey}" encoding="utf-8"/> 8 </target>
target name="-post-compile" 與 -pre-build一樣是需要我們去“重寫”的,等待打包時候關聯調用。
以上就是如何修改Java文件中的變量的方法,此處有一個坑就是我的變量是靜態變量,剛開始修改的時候無論我怎么修改都不起作用,但是文件已刷新我看到確實修改了,
這個問題一度讓我懷疑自己、懷疑整個世界,http://blog.csdn.net/lizeyang/article/details/18218687,是這篇帖子幫我從坑里爬出來了。。。。
所以大家要是在跟隨我的一起探索的時候遇到了問題,請不要氣餒,不要放棄,總會有解決辦法的,我在這個過程中也不知道遇到了多少問題,畢竟我也是一只菜鳥。。。。
OK,繼續我們的需求還剩下一個修改應用的包名:
修改應用的包名有多種方式,網上有一種說法是需要目錄的拷貝,這種方式我嘗試了發現過程相對來說還是很繁瑣的,
我嘗試了一下其實修改應用的包名只是需要修改 AndroidManifest.xml包名以及java文件涉及到的包名(import、package等),如果只修改上述兩處就可以達到效果那有何必要移動文件呢
我測試了,答案是可以的,以下就是現實方式:
1 <target name="changePackName"> 2 <echo>changePackName...</echo> 3 <!--替換AndroidManifest出現的包名 --> 4 <replaceregexp flags="g" encoding="UTF-8" byline="true"> 5 <regexp pattern="package(.*)${old_package_name}"/> 6 <substitution expression="package="${new_package_name}"/> 7 <fileset dir="${basedir}" includes="AndroidManifest.xml"/> 8 </replaceregexp> 9 <!--替換工程中出現的包名 --> 10 <replaceregexp flags="g" encoding="UTF-8" byline="true"> 11 <regexp pattern="import(.*)${old_package_name}.R"/> 12 <substitution expression="import ${new_package_name}.R"/> 13 <fileset dir="${basedir}/src" includes="**/*.java"/> 14 </replaceregexp> 15 </target>
如果想在打包之后還原回去之后就想修改java變量那樣逆着操作一次就OK了,但是要注意 denpends,是要編譯完或者打包完成之后操作。
至此我們的需求就基本已經完成了,下面我貼出主要的兩個文件 ant.properties、custom_rules.xml
ant.properties:
1 #在此文件中定義我們使用到的屬性,中文的話需要轉成UTF-8編碼,可以使用JDK自帶的native2ascii工具轉碼 2 3 #包名 4 application.package=com.tai.antpackage 5 #項目名,缺省時源碼文件名 6 ant.project.name=Autopackage 7 #編碼方式 8 #java.encoding=utf-8 9 10 #編譯輸出絕對路徑 11 out.absolute.dir=d:/MySelf/out 12 #生成文件絕對路徑 13 gos.path=d:/MySelf/out 14 15 16 #簽名key文件絕對路徑 17 key.store=D:/MySelf/a.keystore 18 #簽名文件密碼 19 key.store.password=123456 20 #簽名別稱 21 key.alias=auto1 22 #簽名別稱密碼 23 key.alias.password=123456 24 25 basedir=D:/MySelf/AntPackage 26 xmltasklib.dir=libs 27 manifest.file=AndroidManifest.xml 28 project.name=NewProjectName 29 android.icon=@drawable/ic_launchernew 30 android.app_name=NewProjectName2 31 32 srcdir=src 33 outdir=bin 34 file.replace.path=com/tai/antpackage/constants/ 35 fileName=HttpConstants.java 36 37 UrlKey=${URL} 38 BaseRootKey=${BASEROOT} 39 UrlValue=http://www.cnblogs.com/mauiie/p/5684374.html 40 BaseRootVaue = cnblogs 41 42 old_package_name =com.tai.antpackage 43 new_package_name =com.tai.mauiie
custom_rules.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project name="custom_rules"> 3 <path id="xmltask.classpath"> 4 <pathelement path="${basedir}/${xmltasklib.dir}/xmltask.jar" /> 5 </path> 6 7 <taskdef name="xmltask" classname="com.oopsconsultancy.xmltask.ant.XmlTask" classpathref="xmltask.classpath"/> 8 <target name="-pre-build" depends="changeLogo"> 9 <echo>do something others</echo> 10 </target> 11 12 <target name="changeLogo" depends="changeName" > 13 <echo>changelogo...</echo> 14 <xmltask source="${basedir}/${manifest.file}" dest="${basedir}/${manifest.file}" encoding="utf-8" > 15 <attr path="//manifest/application" attr="android:icon" value="${android.icon}" /> 16 </xmltask> 17 </target> 18 19 <target name="changeName" depends="changeJavaVariable"> 20 <echo>changeName...</echo> 21 <xmltask source="${basedir}/res/values/strings.xml" dest="${basedir}/res/values/strings.xml" encoding="utf-8" > 22 <replace path="/resources/string[@name='app_name']/text()" withText="${android.app_name}"/> 23 </xmltask> 24 </target> 25 26 <target name="changeJavaVariable" depends="changePackName"> 27 <echo>change java file variable value...</echo> 28 <!--copy file="${basedir}/${srcdir}/${file.replace.path}/${fileName}" todir="res" /--> 29 <replace file="${basedir}/${srcdir}/${file.replace.path}/${fileName}" token="${UrlKey}" value="${UrlValue}" encoding="utf-8"/> 30 <replace file="${basedir}/${srcdir}/${file.replace.path}/${fileName}" token="${BaseRootKey}" value="${BaseRootVaue}" encoding="utf-8"/> 31 <delete dir="${basedir}/bin/classes" /> 32 </target> 33 34 <target name="-post-compile" depends="restoreJavaVariable"> 35 <echo>restore java file variable value...</echo> 36 </target> 37 38 <target name="restoreJavaVariable" > 39 <replace file="${basedir}/${srcdir}/${file.replace.path}/${fileName}" token="${UrlValue}" value="${UrlKey}" encoding="utf-8"/> 40 <replace file="${basedir}/${srcdir}/${file.replace.path}/${fileName}" token="${BaseRootVaue}" value="${BaseRootKey}" encoding="utf-8"/> 41 </target> 42 43 44 <target name="changePackName"> 45 <echo>changePackName...</echo> 46 <!--替換AndroidManifest出現的包名 --> 47 <replaceregexp flags="g" encoding="UTF-8" byline="true"> 48 <regexp pattern="package(.*)${old_package_name}"/> 49 <substitution expression="package="${new_package_name}"/> 50 <fileset dir="${basedir}" includes="AndroidManifest.xml"/> 51 </replaceregexp> 52 <!--替換工程中出現的包名 --> 53 <replaceregexp flags="g" encoding="UTF-8" byline="true"> 54 <regexp pattern="import(.*)${old_package_name}.R"/> 55 <substitution expression="import ${new_package_name}.R"/> 56 <fileset dir="${basedir}/src" includes="**/*.java"/> 57 </replaceregexp> 58 </target> 59 60 </project>
最新的這兩個文件可能有部分配置與剛開始貼出來的不一樣是因為我后面的這些是在家中完成的換了電腦。
在嘗試的過程中你可能遇到
如果遇到該錯誤請在你SDK的tools/ant/buidl.xml中查找<property name="aapt.ignore.assets" value="" />
把value設置為:crunch <property name="aapt.ignore.assets" value="crunch" />
如果遇到別的錯誤不要氣餒,不要放棄。
Demo 下載地址:ant 。
后面我會與想要一起學習的效果分享如何使用gradle,完成項目的自動打包。
感謝起初跟我提起這件事的小伙伴,我想做這件事很久了,久到現在使用ant已經落伍了。。。。 。 我是一個拖延症患者。。。。