使用Ant構建Android應用

2014713日,下文中的ADT版本爲adt-bundle-windows-x86_64-20140624

 

上半年項目需要,開發一個基於Android SDK的應用,通過仔細學習Android SDK提供的build.xml文件,發現對Ant的認識上升到了一個新的高度,今天把項目中遇到的知識點記錄下來。

創建build.xml

Android SDK提供了從無到有創建安卓應用工程的命令,但我沒有嘗試過,所以沒有使用經驗。Android SDK裏的工具功能很多、很強大,並且有詳盡的幫助信息,下面對命令的使用方法儘可以查閱幫助,命令樣例如下。

%android_home%\Tools\android -h 

%android_home%\Tools\android -h create project

%android_home%\Tools\android -h update project

創建build.xml文件的步驟:

1) 在ADT裏,創建應用的項目,項目所在目錄名爲比如%ANDROID_PROJ%

2) 調用SDK裏的命令,查看當前SDK支持的平臺,%android_home%\Tools\android list target

3) 調用SDK裏的命令,創建build.xml文件,命令爲%android_home%\Tools\android update project -p %ANDROID_PROJ% -s -t 1

 

準備知識

根據這條命令的輸出,可以知道工具生成了build.xml,同時更新了project.propertiesproguard-project.txtlocal.properties。由於Android項目發展比較快,所以前述文件的格式可能會隨着SDK的版本變化而變化,而這幾個文件非常容易生成,這裏就不再引用各文件的內容了。

繼續之前,先簡單介紹一下Android應用項目裏的文件。

1) project.properties

從文件的註釋可以看出,這個文件是受SDK工具管理的,一般不需要程序員手工修改,看到這裏,相信如果不是爲了給自己惹麻煩的話,建議還是不要自做主張去修改這個文件吧。不過有例外,文件的註釋裏有提到,假如發佈版本時需要啓用proguard工具(proguard做什麼的?),則要手工去掉某行內容的註釋。

從現有的文件內容看,project.properties定義了應用支持的平臺版本,以及依賴庫的目錄。

project.properties對於項目而言很重要,所以Android推薦程序員把這個文件合入到配置管理。從內容看,project.properties文件的內容和程序員本地的環境無關,所在同一個項目的成員應當可以使用同一份配置,不需要單獨做定製。

2) proguard-project.txt

progurad是什麼?從看到project.properties文件裏proguard相關的的註釋起就產生了很大的興趣。從註釋看,proguardSDK自帶的工具,但做什麼的看不出來,不過貌似很重要。從網上的資料得知,proguard工具的主要用途是代碼混淆,提高逆向工程的代價,有效保護應用開發商的核心技術。proguard工具自帶的資料比較詳細,看起來似乎並不困難。

proguard-project.txt文件很重要,需要合入到配置管理。

3) local.properties

這個文件是由SDK工具生成的。目前僅有的用途是配置Android SDK的安裝路徑,這和程序員本地環境配置強相關,因而不建議合入配置管理。此外這個文件並不是必需的,通過閱讀SDK生成的build.xml文件可以發現,定義系統變量ANDROID_HOME並指向SDK的安裝路徑,也可以達到把SDK的安裝路徑傳遞給Ant的目的。

4) ant.properties

通過閱讀build.xml可以發現,如果項目路徑下存在有名爲ant.properties的文件,Ant執行時會自動載入這個文件裏的配置。這裏學到一招,通過Antproperty標籤加載屬性文件,當文件不存在時不會報錯。

5) custom_rules.xml

通過閱讀build.xml可以發現,如果項目路徑下存在名爲custom_rules.xml的文件,Ant會自動加載這個文件裏定義的Target,程序員可以把擴展點的Target定義到這個文件裏,避免build.xml內容過多。這裏學到一招,通過Antimport標籤加載任務文件,可以指定optional屬性爲true,這樣當文件不存在時,也不會報錯。

6) build.xml

構建過程中最重要的文件,出乎意料的是內容非常短,只有短短的不到100行,多數內容還都是註釋,所以閱讀起來不費力氣。主要做了幾件事情,加載local.properties文件,判斷ANDROID_HOME變量是否存在,加載project.properties,加載custom_rules.xml,加載SDK目錄下的build.xml

 

擴展點

這裏要提到Ant的一個比較有意思的特點,build.xml腳本中屬性值基於前置定義優化的原則,即屬性發生重複定義時,前面定義的值不會被後面定義的覆蓋,同理Target也遵守相同的原則,前面定義的Target不會被後面定義的Target覆蓋,這就爲模板腳本的開發工作帶來了不少的便利。

根據這個特點,Android應用的build.xml腳本里定義了兩類擴展點,一類是屬性值,另外一類是預定義的TargetSDK定義的build.xml文件裏預定義好了相關的依賴,項目構建時會自動引用。

程序員自定義的屬性值可以寫到ant.properties裏,而自定義的Target可以定義到custom_rules.xml裏。

 

預定義的屬性

比較慚愧,只用過少數幾個屬性,多數都沒有用到。

adb.device.arg

默認值:空字符串

用途:用於指定adb訪問的設備名稱,除非設備名稱是固定不變的,否則最好的方法還是在執行build.xml時,通過命令行參數的形式傳遞給Ant,樣例如下:

ant -Dadb.device.arg=-d 真實設備

ant -Dadb.device.arg=-e 模擬器

android.package.excludes

默認值:空字符串

用途:針對src目錄設定的過濾規則,避免某些文件被打包進最終的APK中。

 

version.code

默認值:空字符串

沒有嘗試過,不知道是不是可以覆蓋AndroidManifest.xml文件中的定義。

version.name

默認值:空字符串

沒有嘗試過,不知道是不是可以覆蓋AndroidManifest.xml文件中的定義。

aapt.resource.filter

默認值:空字符串

 

aapt.ignore.assets

默認值:空字符串

用途:針對resassets目錄設定的過濾規則,避免目錄內的某些文件被打包進APK。過濾規則的定義:[!][<dir>|<file>][*suffix-match|prefix-match*|full-match],樣例如!.svn:!.git,這個特性還是比較有用的。

dex.force.jumbo

默認值:false

用途:沒用過

dex.disable.merger

默認值:false

用途:沒有用過

java.encoding

默認值:UTF-8

用途:Java代碼編譯時,假如代碼源文件的編碼方式和編譯器指定的編碼方式不同,有可能會導致編譯失敗。

java.target

默認值:1.5

用途:指定編譯源碼時生成class文件的版本。我在項目開發時只用過1.6,但使用最新的SDK編譯,發現1.7也是可以支持的。

java.source

默認值:1.5

用途:源代碼的版本。同上,現在使用新一點的SDK,這裏可以配置爲1.7

java.compilerargs

默認值:空字符串

用途:設置編譯參數。比如啓用lint

<compilerarg value="-Xlint"/>

java.compiler.classpath

默認值:空字符串

用途:設置編譯參數,沒用過。

renderscript.debug.opt.level

默認值:O0

renderscript.release.opt.level

默認值:O3

 

renderscript.support.mode

默認值:false

manifestmerger.enabled

默認值:false

emma.filter

默認值:空字符串

 

verbose

默認值:false

lint.out.html

默認值:bin/lint-results.html

lint.out.xml

默認值:bin/lint-results.xml

 

 

預定義Target

分析SDK提供的模板build.xml文件,可以找到如下的幾個可供擴展用的Target-pre-clean-pre-build-pre-compile-post-compile-post-package-post-build。單個命名就可以理解它們的用途,因而不需要做特別的解釋。

 

編譯目標

我對releasedebugcleaninstrument這幾個Target比較感興趣,所以對它們做了深入的學習。手頭上沒有像樣的工具,截圖是用XMind畫的,看起來效果還不錯。綠色的笑臉表示可由程序員自定義擴展的Target。但是從Word裏向網頁裏貼圖失敗,所以只好上傳附件。

 

後記

安卓SDK裏提供的build.xml只有大約1500行,但從中可以看出Google爲了推廣Android項目的確是不遺餘力,在ADT的方方面面都投入了相當的精力,以儘可能的降低安卓程序員入門的門檻,提高安卓應用的開發效率。有這樣的模板build.xml文件,程序員開發安卓應用時,基本不需要在打包腳本上投入太多的精力就可以完成相關工作。

 

調試Ant腳本

eclipse裏調試Ant腳本很方便,方法和調試Java代碼一樣。如果直接在命令行裏檢查Ant腳本的正確性,就比較麻煩、費時了。好在Ant在運行時提供了日誌,通過增加-d選項,可以讓Ant輸出比較平常更多的信息,對於大型項目來說,這些信息對理解腳本在運行時的行爲會非常有幫助。

 

輸出路徑變量的值

走讀Android提供的模板腳本時,經常會看到變量project.all.jars.path。這個變量用於表示當前項目加載的全部Jar文件列表,但在安卓的模板腳本里沒有提供定義,而且使用通常的echo 標籤不能正常的把取值輸出到標準輸出。怎樣才知道這個變量在腳本運行期的取值呢?有個簡單的方法,使用Ant1.9版本,可以通過如下方式把路徑變量的值輸出到標準輸出。

<echo>${toString:project.all.jars.path}</echo>

<echo>${project.all.jars.path}</echo>   <!-- 這行腳本無效 -->

在我的測試工程裏輸出如下,可以看到通常的echo標籤沒有生效。

-post-compile:

     [echo] E:\Android\adt\workspace\JackieFrog\libs\android-support-v4.jar

     [echo] ${project.all.jars.path}

 

運行時修改路徑變量取值

比如在腳本運行時,希望動態修改路徑變量project.all.jars.path的值,便於控制參與編譯過程的Jar文件。這時可以利用安卓編譯模板提供的預定義擴展點,在某個Target執行前把project.all.jars.path變量的值做調整。如下是樣例

<target name="-post-compile">

<echo>${toString:project.all.jars.path}</echo>

    <path id="project.all.jars.path">

        <pathelement path="${sdk.dir}/tools/lib/ant-tasks.jar" />

    </path>

<echo>${toString:project.all.jars.path}</echo>

</target>

如下是樣例輸出

-post-compile:

     [echo] E:\Android\adt\workspace\JackieFrog\libs\android-support-v4.jar

     [echo] E:\Android\adt\sdk\tools\lib\ant-tasks.jar

 

APK簽名

根據Android平臺的安全要求,任何APK必須被簽名,然後才允許被安裝到終端上。當使用eclipse調試時,eclipse使用默認的私鑰爲APK簽名,當然程序員通過配置自己的私鑰來替換掉默認的私鑰。

ant.properties文件中增加如下變量的定義:

key.store=app.keystore             #私鑰文件
key.alias=www.example.com        #私鑰的別名

key.store.password=app.password    #私鑰的存儲口令
key.alias.password=app.password    #私鑰別名的口令

 

私鑰可以預先生成,後續每次打包都使用相同的,也可以在每次打包時都生成新的。

custom_rules.xml文件中擴展-post-package

集成lombok

使用Lombok好處很多,這裏不一一介紹,只講解在Android應用項目中集成Lombok的方法。

步驟比較簡單,如下操作:

1、下載lombok,路徑http://projectlombok.org/downloads/lombok.jar

2、爲ADT裏的eclipse安裝lombok,方法非常簡單,具體可以問度娘;

3、生成接口Jar,命令java -jar lombok.jar publicApi,得到lombok-api.jar

4、把lombok-api.jar複製到安卓應用項目下的libs目錄;

5、在安卓應用項目裏創建一個名爲compile-libs的目錄,把lombok.jar放到compile-libs目錄下;

6、在-post-compile中調整變量project.all.jars.path,避免把lombok-api.jar或者lombok.jar也打包進應用裏。

條件編譯

這個話題和Ant關係不大,只是附帶提一下。Java語言沒有提供類似C/C++的預處理能力,但有時真覺得的不方便,比如對於一些調試用的日誌,出於性能或者其它方面的考慮,僅希望在測試時存在,版本發佈之後就不希望它們再出現,這時就很麻煩,想點其它辦法來解決。

 

如下是SDK生成的代碼樣例。

 

/** Automatically generated file. DO NOT MODIFY */

package jackie.garden.tools;

 

public final class BuildConfig {

    public final static boolean DEBUG = true;

}

 

上述代碼有什麼意義呢?先看下面一段代碼。

 

public class Main {

 

private static final boolean debug = false;

/**

 * @param args

 */

public static void main(final String[] args) {

 

if (debug) {

System.out.println("hello, debug is true");

} else {

System.out.println("hello, debug is false");

}

}

}

 

代碼比較簡單,運行輸出結果也是確定的,但奇妙的地方不在結果,而在於編譯後生成的字節碼。使用JD-GUI查看上述代碼編譯後得到的class文件,可以得到反編譯後的代碼如下所示。

import java.io.PrintStream;

 

public class Main

{

  private static final boolean debug = false;

  

  public static void main(String[] args)

  {

    System.out.println("hello, debug is false");

  }

}

代碼源文件中的if判斷消失了,難道javac在生成字節碼時,直接忽略了源文件中的死代碼?爲了證實這一假設,可以嘗試修改源文件中debug的值,重新用JD-GUI查看生成的字節碼,操作過程比較簡單,也非常有意思,這裏就不再附上源碼。

 

 

參考文檔

Android開發者官網

http://www.cnitblog.com/zouzheng/archive/2011/01/12/72638.html

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章