[時間:2017-09] [狀態:Open]
[關鍵詞:Android,Android Studio,gradle,jar,aar,library]
0 引言
最近在工作中遇到了升級Android Studio 2.3.3穩定版之后,無法編譯jar包的問題。之后尋找AS文檔-探索 Android Studio發現。可以通過AS創建和編譯jar包,順便看到支持原生開發,可以直接在AS中調試c/c++代碼,這是非常不錯的功能。終於可以擺脫純打日志的開發環境了。
本系列文章也就因此而出現。我希望閱讀文本文之后,大家能夠基本了解如何使用新版的Android Studio開發原生引用。
這里說明一點,本文是參考谷歌的Android Studio官網來的,也可以認為是一種翻譯版。可以訪問的話你可以直接查看對應內容。
這是第三篇:使用Android Studio創建庫文件(Java)。(嚴格來說這跟原生開發沒什么關系,但是為了完整的完成我最初的目標,還是整理下此文吧。)
1 環境准備
請參考本系列的第一篇:原生庫示例創建及驗證中的要求,安裝和更新Android Studio。
2 Android庫簡介
Android庫在結構上與app module等價。它可以包含所有用於構建app的東西,包括源碼、資源文件和Android manifest。Android庫不是直接編譯成可在設備上執行的APK文件,而是編譯為AAR(Android Archive)文件,你可以將該文件作為app module的依賴項。和JAR包不同的是,AAR文件可以包含Android資源和manifest文件,這就是說除了Java類和方法之外,AAr還支持打包諸如layouts和drawables之類的共享資源。
Android庫模塊在以下情況下很有用:
- 當你在構建多個使用相同組件的app時,比如Activity、service或UI layout。
- 當你基於多個APK變種構建app時,例如免費版和付費版,他們共享同樣的核心組件。
在以上了兩種情況中,只要簡單的把需要重用的文件放到庫模塊中,然后在app module中添加該庫作為依賴項就可以了。本文將告訴你如何實現這些。
3 新建庫模塊
AS中新建一個庫模塊,步驟如下:
- 點擊菜單中的 File > New > New Module
- 在Create New Module窗口中,選擇Android Library,點擊Next。
- 這里也可以選擇新建Java庫,也就是構建傳統的JAR包。
- 給你的庫選擇一個名字,並選擇一個最低SDK版本,之后點擊Finish。
當Gradle完成工程同步,庫模塊就會顯示在工程面板中。
4 將現在app module轉為庫模塊
可以參考下列步驟:
-
打開module層的build.gradle文件
-
刪除applicationId這一行,這個標志只能在Android app module中定義。
-
在該文件的開頭,你應該能看到下面內容:
apply plugin: 'com.android.application'
將其更改為:
apply plugin: 'com.android.library'
-
保存該文件,並點擊菜單中的Tools > Android > Sync Project with Gradle Files。
這就搞定了。整個模塊的結構是不變的,但現在它是以Android庫運行的,構建時會新建一個AAR文件,而不是APK。
5 配置AAR作為依賴項
在其他app module中使用Android庫時,需要按照下面方式配置:
- 使用以下兩種方式將庫添加到你的工程中(如果你在同一個工程中新建了庫模塊,你可以跳過此步驟):
- 添加編譯好的AAR/JAR文件(該庫必須已經構建好)
- 點擊菜單中的File > New > New Module。
- 點擊Import .JAR/.AAR Package,並點擊Next。
- 輸入編譯好的AAR/JAR文件的路徑,並點擊Finish。
- 將庫模塊導入到你的工程(庫源代碼將成為你工程的一部分)
- 點擊菜單中的File > New > Import Module.
- 輸入庫模塊的目錄,並點擊Finish。
The library module is copied to your project, so you can actually edit the library code. If you want to maintain a single version of the library code, then this is probably not what you want and you should instead add the compiled AAR file as described above.
- 添加編譯好的AAR/JAR文件(該庫必須已經構建好)
- 檢查下你的settings.gradle文件中新加入的庫是否在其中,如下面所示添加了"my-library-module":
include ':app', ':my-library-module'
- 打開app module的build.gradle文件,並在dependencies塊中添加一行,如下代碼片段所示:
dependencies { compile project(":my-library-module") }
- 點擊Sync Project with Gradle Files
在上面示例中,編譯配置中添加了一個名為my-library-module的庫作為整個app module的構建依賴項。如果你希望在特定構建變量上需要該庫,不要使用compile,而是使用buildVariantNameCompile命令。例如,如果你只想在"pro"產品中包含該庫,你需要這樣編寫:
productFlavors {
pro { ... }
}
dependencies {
proCompile project(":my-library-module")
}
通過上述設置之后,Android庫中所有的代碼和資源都可以在app module中訪問了,在構建時,對應庫的AAR文件會被打包到APK中。
如果你需要單獨共享AAR文件,你可以在project-name/module-name/build/outputs/aar/中找到它,你可以通過菜單中的Build > Make Project重新構建它。
7 在庫中發布非默認的variant
默認情況下,庫模塊僅發布和暴露"release"構建variant下的資源給其他Android工程/module。也就是說,如果app module將該庫作為依賴項,即使在debug版本下的app也只能訪問該庫的"release"variant。然后,你可以通過在庫的build.gradle文件中添加下面命令使得Gradle可以訪問"debug"構建variant下的資源:
android {
...
// Sets the "debug" build variant as the default variant
// of the library that Gradle should publish.
defaultPublishConfig "debug"
}
這樣配置之后,Gradle將發布"debug"構建variant下的資源。然而,如果庫模塊使用product flavors,你就需要配defaultPublishConfig屬性,並使用全配置名來指定構建variant(否則,Gradle將不會發布你的庫,因為傳統的"release"及"debug" variant已經不存在了)。下面示例展示了如何組合使用 "demo" product flavor和"debug"構建類型的導出構建variant:
android {
...
defaultPublishConfig "demoDebug"
}
謹記:如果你正在構建用於發布app的release版本,你需要修改defaultPublishConfig以使用庫的release variant對應的資源。同樣的,你可以設置Gradle來發布和暴露庫中所有可用的variant,同時每個app variant僅使用其需要variant的資源。在庫的build.gradle文件中添加以下行可以讓Gradle發布庫中所有的variants:
android {
...
// Tells Gradle to build all variants of the library. Note that this
// may increase build times because Gradle must build multiple AARs,
// instead of only one.
publishNonDefault true
}
現在你可以通過修改你的app的build.gradle文件的dependencies塊來訪問庫中所有可見的variants。下面位於app module的build.gradle文件中的代碼片段指示Gradle在構建app的"demoDebug"版本時使用庫的"demoDebug" variant,並在構建app的"fullRelease"版本時使用"fullRelease" variant。
android {...}
...
// Creates Gradle dependency configurations to use in the dependencies block.
configurations {
// Initializes placeholder configurations that the Android plugin can use when targeting
// the corresponding variant of the app.
demoDebugCompile {}
fullReleaseCompile {}
...
}
dependencies {
// If the library configures multiple build variants using product flavors,
// you must target one of the library's variants using its full configuration name.
demoDebugCompile project(path: ':my-library-module', configuration: 'demoDebug')
fullReleaseCompile project(path: ':my-library-module', configuration: 'fullRelease')
...
}
8 選擇作為public的資源
默認情況下庫中所有的資源都是public的。為了將所有資源默認設置為private,你需要至少定義一個特殊屬性作為public。資源包括你的工程下的res目錄中的所有文件。為了阻止庫的用戶訪問僅供內部使用的資源,你需要使用自動private設計機制,聲明一個或多個public資源。同時,你可以把所有資源設置為private,只要添加一個空的<public />
標簽即可,這個標簽主要是標記不存在public資源,也就是所有資源都是private的。
為了聲明一個public資源,在庫的public.xml中添加一個
下面示例代碼創建了兩個public字符串:mylib_app_name和mylib_public_string:
<resources>
<public name="mylib_app_name" type="string"/>
<public name="mylib_public_string" type="string"/>
</resources>
你需要保證中那些使用你的庫的開發者可以訪問的資源設置為public。例如,v7 appcompat庫中大多數資源是private的,但是控制Toolbar widget的屬性是public的,以支持material design。
隱式地將屬性設置為private不僅可以防止庫的用戶得到內部庫資源的代碼自動補全提示,而且允許你在不破壞客戶端的前提下重命名或刪除private資源。private資源會被代碼自動補全和theme editor自動過濾掉,並且Lint在你引用private資源時會給出警告信息。
在構建庫時,Android Gradle插件將會獲取public資源定義,並將其提取到public.txt文件中,此文件會被打包到AAR文件中。
9 開發考量
在你開發庫模塊和依賴庫的app時,請留心以下行為和限制。
一旦在Android app module中添加了對庫模塊的引用,你就可以設置其相對優先級。在編譯時,庫模塊會一個個合並到app中,按照優先級的高低順序合並。
- 資源合並沖突
構建工具會將庫模塊的資源和其所依附的app module進行合並。如果給定的資源ID在兩個module中都有定義,則使用app中的資源。
如果在多個AAR庫中發生沖突,那么在dependencies列表中第一個出現(在dependencies塊的頂部)的資源會被使用。
為了避免通用資源ID的沖突,可以考慮使用前綴或者統一命名方案,以保證每個module的名字都是唯一的(或在整個工程module中是唯一的)。 - 庫模塊可以包含JAR包
你可以開發一個本身包含JAR包的庫模塊;然而,你需要對依賴該庫的app module進行手工編輯構建路徑,並添加該JAR包所在路徑。 - 庫模塊可以依賴外部的JAR包
你可以開發一個依賴外部庫的庫模塊。(例如,Maps外部庫)。在這種情況下,依賴該庫的app在構建時必須使用一個包含該外部庫(例如,Google API Add-on)的target。需要注意的是,在庫模塊和app module的manifest文件中必須聲明該外部庫,使用元素。 - 庫模塊不能包含原始asset
在庫模塊中,工具並不支持使用原始asset文件。app中使用的任何asset資源必須保存在其assets/目錄下。 - app module的minSdkVersion必須大於等於庫中定義的版本。
一個庫被編譯為app module的一部分,所以在庫中使用的API必須與app module支持的平台版本兼容。 - 每個庫模塊都可以創建單獨的R類
在你構建有庫依賴的app module時,庫模塊首先被編譯到AAR文件中,然后添加到app module中。因此,每個庫都有其自己的R類,其命名是按照庫的包名確定的。從主模塊和庫模塊生成的R類在所有包中創建,這些都是會包含在主模塊包中和庫的包中。 - 庫模塊可以包含自己的ProGuard配置文件
你可以在庫上實現代碼縮減,通過在你的庫中添加ProGuard配置文件,其中包含了ProGuard指令。構建工具會把這個文件嵌入到生成的AAR文件中。當你把該庫添加到app module之后,庫的ProGuard文件會被添加到app Module的ProGuard配置文件(proguard.txt)中。
通過在你的庫模塊中嵌入一個ProGuard文件,你可以確保那些依賴於該庫的app module在使用你的庫是無需手動更新他們的ProGuard文件。當ProGuard在Android app module上執行時,它會自動使用來自app module和庫中的指令,這樣你就不用單獨執行庫中的配置文件了。
為了指定你的庫配置文件的名字,在consumerProguardFiles方法中添加下參數,該方法在你的庫的build.gradle文件中的defaultConfig塊中。例如,下面代碼段將lib-proguard-rules.txt作為該庫的ProGuard配置文件:
android {
defaultConfig {
consumerProguardFiles 'lib-proguard-rules.txt'
}
...
}
為了保證你的庫中的ProGuard規則不會對app module中的代碼縮減產生副作用,只包含那些在你的庫中不工作的禁用ProGuard功能的規則。那些用於輔助開發者的規則可能會與app module或其他庫有沖突,因此不可以包含類似規則。例如,你庫的ProGuard文件可以指定在app module最小化時那些代碼可以保留。
- 測試庫模塊和測試app一樣
主要區別在於庫和它的依賴項是自動作為測試APK的依賴項的。這意味着,測試APK包含了不僅僅是它自身的代碼,還有庫的AAR及庫的依賴項。因為並不存在獨立的“測試下的app”,androidTest任務僅安裝(和卸載)測試APK。
在合並多個manifest文件時,Gradle將遵循默認優先級規則,並將庫的manifest依次合並到測試APK的主manifest中。
10 AAR文件剖析
AAR文件的文件擴展名是.aar,對應的Maven生成的類型也應該是aar。該文件本身是一個zip文件,包含下列必須條目:
/AndroidManifest.xml
/classes.jar
/res/
/R.txt
/public.txt
另外,AAR文件可以包含一個或多個下列可選條目:
/assets/
/libs/name.jar
/jni/abi_name/name.so (where abi_name is one of the Android supported ABIs)
/proguard.txt
/lint.jar
11 小結及后續
本文作為Android創建的第三篇,整體比較簡單,內容主要是翻譯部分,整理並介紹了如何使用AS構建JAR包。僅供后續參考。