Android Studio分模塊自動化構建實戰
最近在使用Android Studio+Gradle做一個基礎框架SDK項目,該框架主要實現每個app都需要的基礎能力,例如網絡請求,圖片緩存,json解析,日志記錄等等。
眾所周知,AndroidStudio中應該盡量使用Module來進行模塊的劃分,既能達到模塊解耦的目的,也能在必要的時候輕松實現分模塊打包,特別是在SDK項目中。那么什么是分模塊打包呢?就是我們可以根據第三方使用者的需求,自動化的提供SDK的全量版本,部分功能版本以及最小功能版本等等。
我們的項目結構如下所示,每個功能獨立成一個Module:

由于我們的模塊都是純代碼的,沒有包含資源文件,因此不是以aar包的形式而是使用jar包形式對外提供。順便提一句,生成的aar包默認路徑是:
build/output/aar/
而jar包可以到如下路徑尋找:
build/intermediates/bundles/debug/classes.jar build/intermediates/bundles/release/classes.jar
Jar包的合并
從項目工程截圖中可以看到,我們的project包含多個module,每個基礎功能的module最終編譯生成的都是一個 classes.jar。因此project最終會生成一堆的jar包,而到了對外發布時,我們要提供一個單獨的jar包出去,因此,就需要對jar包進行合并。很不幸,Android Studio沒有提供這樣的功能,因此只能靠自己寫腳本調用jar命令來實現了,打開命令行terminal,輸入jar,就可以打印出jar的用法,如下所示:
guhaoxindeMacBook-Pro:~ guhaoxin$ jar 用法: jar {ctxui}[vfm0Me] [jar-file] [manifest-file] [entry-point] [-C dir] files ... 選項包括: -c 創建新的歸檔文件 -t 列出歸檔目錄 -x 從檔案中提取指定的 (或所有) 文件 -u 更新現有的歸檔文件 -v 在標準輸出中生成詳細輸出 -f 指定歸檔文件名 -m 包含指定清單文件中的清單信息 -e 為捆綁到可執行 jar 文件的獨立應用程序 指定應用程序入口點 -0 僅存儲; 不使用情況任何 ZIP 壓縮 -M 不創建條目的清單文件 -i 為指定的 jar 文件生成索引信息 -C 更改為指定的目錄并包含其中的文件 如果有任何目錄文件, 則對其進行遞歸處理。 清單文件名, 歸檔文件名和入口點名稱的指定順序 與 'm', 'f' 和 'e' 標記的指定順序相同。示例 1: 將兩個類文件歸檔到一個名為 classes.jar 的歸檔文件中: jar cvf classes.jar Foo.class Bar.class 示例 2: 使用現有的清單文件 'mymanifest' 并 將 foo/ 目錄中的所有文件歸檔到 'classes.jar' 中: jar cvfm classes.jar mymanifest -C foo/ .</pre>
使用jar命令,主要實現兩個功能:
- 將所有jar包的class文件解壓到某個目錄中
- 將解壓后所有class文件的重新壓縮為一個單獨的jar包 </ul>
由于jar命令不能指定最終輸出的目錄,因此我們需要首先cd到用于存放解壓后class文件的一個臨時目錄,然后依次對所有jar包進行解壓操作,解壓命令如下所示:
jar -xvf ../../hfasynchttp/build/intermediates/bundles/debug/classes.jar
當所有的jar包都解壓完畢后,接著執行壓縮命令,這樣就得到一個單獨的jar包了:
jar -cvfM AndroidHyperion_${version}_debug.jar .
分模塊自動化構建
自動化構建包括本地構建和Jenkins構建兩部分,本地構建主要用于開發自己調試使用,Jenkins構建主要用于測試,產品等取包以及跑Monkey使用。
本地構建
本地構建腳本文件位于工程根目錄下的build_local.sh,該腳本的主要功能有:
- 調用gradlew命令執行gradle編譯,生成各個Module的jar包
- 解壓各個Module生成的jar包,并把解壓后的class文件重新打包成單獨的一個jar包
- 分模塊打包功能通過定義Boolean變量值進行控制
- 輸出目錄是output </ul>
build_local.sh文件內容如下:
#!/bin/sh使用Gradle編譯各個module
./gradlew clean ./gradlew build --stacktrace --debug
進入輸出目錄
cd output
清空輸出目錄
rm -rf *
創建輸出子目錄
mkdir temp mkdir debug mkdir release
定義sdk版本號
version="1.0.0"
定義模塊是否打包標識
is_include_hfasynchttp=true is_include_bitmapfun=true is_include_hfjson=true is_include_hflogger=true
省略其他...
解壓所有debug版本的jar包到temp目錄中
cd temp
if $is_include_hfasynchttp; then jar -xvf ../../hfasynchttp/build/intermediates/bundles/debug/classes.jar fi
if $is_include_bitmapfun; then jar -xvf ../../hfbitmapfun/build/intermediates/bundles/debug/classes.jar fi
if $is_include_hfjson; then jar -xvf ../../hfjson/build/intermediates/bundles/debug/classes.jar fi
if $is_include_hflogger; then jar -xvf ../../hflogger/build/intermediates/bundles/debug/classes.jar fi
壓縮所有debug版本的class文件到一個獨立的jar包中
jar -cvfM AndroidHyperion_${version}_debug.jar .
拷貝文件
mv AndroidHyperion_${version}_debug.jar ../debug
清空temp目錄
rm -rf *
解壓所有release版本的jar包到temp目錄中
if $is_include_hfasynchttp; then jar -xvf ../../hfasynchttp/build/intermediates/bundles/release/classes.jar fi
if $is_include_bitmapfun; then jar -xvf ../../hfbitmapfun/build/intermediates/bundles/release/classes.jar fi
if $is_include_hfjson; then jar -xvf ../../hfjson/build/intermediates/bundles/release/classes.jar fi
if $is_include_hflogger; then jar -xvf ../../hflogger/build/intermediates/bundles/release/classes.jar fi
壓縮所有release版本的class文件到一個jar包中
jar -cvfM AndroidHyperion_${version}_release.jar .
拷貝文件
mv AndroidHyperion_${version}_release.jar ../release
刪除temp目錄
cd .. rm -rf temp</pre>
Jenkins構建
Jenkins編譯腳本文件位于工程根目錄下的build_jenkins.sh,該腳本的主要功能有:
- 調用gradlew命令執行gradle編譯,生成各個Moudle的jar包
- 解壓各個Module生成的jar包,并把解壓后的class文件重新打包成單獨的一個jar包
- 分模塊打包功能通過Jenkins上面配置的參數化構建參數進行控制
- 輸出目錄是output </ul>
可以看到,和本地構建唯一的區別就是分模塊的參數化構建參數是定義在Jenkins上的,而不是定義在本地腳本中的,為了完整清晰起見,我們還是把完整的腳本文件貼出來:
#!/bin/sh./gradlew clean ./gradlew build --stacktrace --debug
進入輸出目錄
cd output
清空輸出目錄
rm -rf *
創建輸出子目錄
mkdir temp mkdir debug mkdir release
cd temp
解壓所有debug版本的jar包
if $is_include_hfasynchttp; then jar -xvf ../../hfasynchttp/build/intermediates/bundles/debug/classes.jar fi
if $is_include_bitmapfun; then jar -xvf ../../hfbitmapfun/build/intermediates/bundles/debug/classes.jar fi
if $is_include_hfjson; then jar -xvf ../../hfjson/build/intermediates/bundles/debug/classes.jar fi
if $is_include_hflogger; then jar -xvf ../../hflogger/build/intermediates/bundles/debug/classes.jar fi
壓縮所有debug版本的class文件到一個jar包中
jar -cvfM AndroidHyperion_${version}_debug.jar .
移動生成的jar包到debug目錄
mv AndroidHyperion_${version}_debug.jar ../debug
清空temp目錄
rm -rf *
解壓所有release版本的jar包
if $is_include_hfasynchttp; then jar -xvf ../../hfasynchttp/build/intermediates/bundles/release/classes.jar fi
if $is_include_bitmapfun; then jar -xvf ../../hfbitmapfun/build/intermediates/bundles/release/classes.jar fi
if $is_include_hfjson; then jar -xvf ../../hfjson/build/intermediates/bundles/release/classes.jar fi
if $is_include_hflogger; then jar -xvf ../../hflogger/build/intermediates/bundles/release/classes.jar fi
壓縮所有release版本的class文件到一個jar包中
jar -cvfM AndroidHyperion_${version}_release.jar .
移動生成的jar包到release目錄
mv AndroidHyperion_${version}_release.jar ../release
刪除temp目錄
cd .. rm -rf temp</pre>
local和Jenkins參數化構建參數定義
類型 | 名稱 | 默認值 | 描述 |
---|---|---|---|
String | version | 1.0.0 | Hyperion sdk版本號 |
Boolean | is_include_hfasynchttp | true | 是否打包hfasynchttp |
Boolean | is_include_bitmapfun | true | 是否打包hfbitmapfun |
Boolean | is_include_hfjson | true | 是否打包hfjson |
Boolean | is_include_hflogger | true | 是否打包hflogger |