轉自:http://chenzhongjin.cn/2016/06/23/正確的姿勢來優(yōu)化你的build-gradle/
引言
eclipse 已經成功了過去式.人人 Android Studio 時代已經到來.面對 gradle .很多人都誠惶誠恐.甚至很多時候github clone下來的項目都不會編譯.gradle version尔许、gradle plugin version、sdk version、support version 的概念都沒有分清楚.導致因為跟原作者的一些配置和本地擁有的環(huán)境不匹配而無法編譯成功.剛好公司項目到了一個階段性手尾.趁著有一點時間.特地記錄一下.和大家一齊分享交流.
下面統(tǒng)一用我寫的一個RecyclerView的開源項目 RvHelper 來作為sample代碼:RvHelper-github
統(tǒng)一管理所有依賴
在項目中新建一個buildsystem文件夾.并且新建一個dependencies.gradle文件.文件夾名稱和文件名字隨意.自己使用的時候映射即可.如圖:
然后再進行文件的編寫
ext.versions = [
code : 1,
name : '1.0.0',
minSdk : 15,
targetSdk : 23,
compileSdk : 23,
buildTools : '23.0.3',
// Library versions
junit : '4.12',
supportLibs: '23.4.0',
multidex : '1.0.1',
]
ext.libraries = [
junit : "junit:junit:$versions.junit",
supportV4 : "com.android.support:support-v4:$versions.supportLibs",
supportAppCompat : "com.android.support:appcompat-v7:$versions.supportLibs",
supportDesign : "com.android.support:design:$versions.supportLibs",
supportRecyclerView : "com.android.support:recyclerview-v7:$versions.supportLibs",
supportCardView : "com.android.support:cardview-v7:$versions.supportLibs",
supportAnnotations : "com.android.support:support-annotations:$versions.supportLibs",
//拆dex
multidex : "com.android.support:multidex:1.0.1",
]
上面只是提供了最簡單的一些依賴的配置.這樣做的好處就是.如果存在多個module.同時都依賴相同的 minSdk & targetSdk & compileSdk & buildTools & supportLibs 就變得非常的方便.比較好管理.
gradle version說明
首先.對于項目來講.gradle version會在如下的文件里面進行聲明.
可以看到我目前使用的版本是 gradle2.10.當然.其實本身下載Android Studio的時候自帶也有一個默認的Gradle.如圖:
不過如果你想自己想嘗鮮的話.也可以到gradle官網(wǎng)進行下載.
下載好之后配置一下環(huán)境變量即可.
配置好之后也可以在項目中指定gradle文件夾.如圖:
gradle plugin version說明
很多時候.gradle plugin版本和本地的as自帶的版本不對應.然后剛好又被墻了.沒有自備梯子.就導致無法編譯.所以在open project之前對應自己本地as的版本來修改好是很重要的一件事.
首先.打開項目根目錄的 build.gradle 如圖:
類似的.現(xiàn)在我的as版本是 2.1.2.那么.我的gradle plugin version就應該對應就是
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.1.2'
}
}
如果我的as版本是2.2pv3.那么將會是
classpath 'com.android.tools.build:gradle:2.2-alpha3'
當然其實本身如果你剛好在用as2.2pv3的話.其實你打開項目的時候.他就會機智的彈出讓你升級的選擇框了.
module中使用統(tǒng)一配置
首先需要在根目錄的 build.gradle 中聲明一下引入我們自己編寫的配置單.
然后在對應自己項目的Android-Lib-Moduel或App-Module就可以直接這么使用.
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile libraries.junit
compile libraries.supportAppCompat
compile libraries.supportRecyclerView
}
這樣就可以相對優(yōu)雅的處理好各種依賴的版本一致性問題.
優(yōu)雅的管理so庫
很多時候第三方sdk都會提供多個版本的so庫.而因為可能我們同時都會使用不同的sdk.然后有一些剛好沒提供上.而我們想要正常使用.理應取所有so庫的交集目錄.
如:
armeabi
sdk1.so
sdk2.so
armeabi-v7a
sdk1.so
這種情況下.我們只能使用armeabi的目錄.而刪除掉 armeabi-v7a.因為如果你剛好apk安裝到了架構是 armeabi-v7a 的手機上.他就會解壓apk中的 armeabi-v7a 目錄到/data/data/xxApp/lib. 而顯然sdk2.so是不存在的.這個時候如果你代碼有使用到sdk2.so的話.必然會出現(xiàn)找不到so庫的問題.從而導致crash.
當然.我們可以直接只刪除到剩下 armeabi文件夾.但是這種方式就有點麻煩.因為如果剛好sdk2.so我們發(fā)現(xiàn)業(yè)務不需要了.可以刪了.而sdk1.so我們是有多個平臺編譯的so庫了.為了更加好的效果.我也不在于apk大一點.這種情況下.又要把 armeabi-v7a 的東西copy回來.這樣也太麻煩了.所以.我們通過gradle來實現(xiàn).
首先.編輯項目根目錄下的 gradle.properties文件.
添加一行
...
android.useDeprecatedNdk=true
然后.在app-module的 build.gradle 中
android {
//...
defaultConfig {
//xx
ndk {
//設置支持的SO庫架構
abiFilters 'armeabi'//, 'armeabi-v7a', 'arm64-v8a'//, 'x86', 'x86_64'
}
}
}
直接隨便根據(jù)自己需求來配置需要打包到apk的so庫類型.
自定義生成的apk名稱
as中默認只是生成app-debug.apk.這樣看起來太單調了.看起來一點都不nice.所以我們都喜歡自定義打包生成的apk的名稱能夠自定義.很簡單.只需要在app-module的build.gradle中配置
android {
//...
applicationVariants.all { variant ->
variant.outputs.each { output ->
output.outputFile = new File(
output.outputFile.parent + "/${variant.buildType.name}",
"dreamliner-rvhelperSample-${variant.buildType.name}-V${variant.versionName}.apk".toLowerCase())
}
}
}
效果如下:
配置正式簽名
很多時候我們接入微信/高德地圖.都需要進行正式簽名才可以正常校驗成功和使用.as本身也提供了配置簽名的可視化操作.
如圖:
當然.我們更喜歡的是自己寫配置清單.
首先在app-module下創(chuàng)建自己的簽名和對應的密碼.別名.別名密碼等配置.
jks的創(chuàng)建就不詳細描述了.自行google.主要講的是配置 private.properties文件.
補充相關內容:
RELEASE_STORE_FILE=sample.jks //簽名的名稱
RELEASE_STORE_PASSWORD=123456 //簽名的密碼
RELEASE_KEY_ALIAS=sample //別名名稱
RELEASE_KEY_PASSWORD=123456 //別名的密碼
然后在使用的app-module的build.gradle下再進行配置.
android{
//...
signingConfigs {
release {
def filePrivateProperties = file("private.properties")
if (filePrivateProperties.exists()) {
Properties propsPrivate = new Properties()
propsPrivate.load(new FileInputStream(filePrivateProperties))
storeFile file(propsPrivate['RELEASE_STORE_FILE'])
storePassword propsPrivate['RELEASE_STORE_PASSWORD']
keyAlias propsPrivate['RELEASE_KEY_ALIAS']
keyPassword propsPrivate['RELEASE_KEY_PASSWORD']
}
}
}
}
然后直接在buildType中指定一下使用什么簽名即可.
android{
buildTypes {
debug {
minifyEnabled false
signingConfig signingConfigs.release //指定使用上面配置好的簽名內容
}
release {
minifyEnabled true
shrinkResources true
signingConfig signingConfigs.release
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
preview {
debuggable true // save debug mes
minifyEnabled true
shrinkResources true
signingConfig signingConfigs.release
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
常用的差異化構建
比如我現(xiàn)在喜歡在debug的時候.我的app_name是rvHelper-debug.而到正式的時候就用rvHelper.這樣自己使用的時候也能一眼就看出來在使用的是測試/正式的包.
android{
buildTypes {
debug {
resValue 'string', 'APP_NAME', 'rvHelper-debug'
resValue 'string', 'isDebug', 'true'
}
release {
resValue 'string', 'APP_NAME', 'rvHelper'
resValue 'string', 'isDebug', 'false'
}
preview {
resValue 'string', 'APP_NAME', 'rvHelper-preview'
resValue 'string', 'isDebug', 'true'
}
}
}
然后直接在 string.xml 中進行配置
<string name="app_name">@string/APP_NAME</string>
而在清單文件上面當然使用 @string/app_name.這樣一配置下來.就可以實現(xiàn)你想要的差異構建的效果.
同理.我想根據(jù)debug/release來初始化的時候做不同的邏輯.比如在Application初始化的時候.
public class AppContext extends Application {
private static AppContext mInstance;
private boolean isDebug;
public static AppContext getInstance() {
return mInstance;
}
@Override
public void onCreate() {
super.onCreate();
mInstance = this;
initDebug();
initNet();
//如初始化net一樣.把相關的圖片加載框架/Log/其他有debug開關的一些依賴.都可以進行差異化構建
}
public boolean isDebug() {
return isDebug;
}
private void initDebug() {
isDebug = Boolean.parseBoolean(getString(R.string.isDebug));
}
private void initNet() {
if (isDebug()) {
//使用本地服務器/測試服務器的地址
} else {
//使用生成服的服務器地址
}
}
}
這樣就可以很好的進行差異化構建[和多渠道打包同一個概念]
FAQ
開啟multidex的常見錯誤
在我之前的博客中有簡單介紹過如何配置multidex.但是很多時候編譯的時候就會因為javaMaxHeapSize不足導致無法編譯成功.這個時候只需要加入一下配置即可.
android {
//...
dexOptions {
preDexLibraries = false
javaMaxHeapSize "4g"
}
}
exclude重復的jar版本等信息
很多時候我們用不同的jar包.里面都會有版本信息/日志等等文件會一齊進行打包.如果剛好同名.就會導致打包的時候出錯.這個時候只需要根據(jù)報錯提供的名稱來進行exclude即可.如下:
android {
//...
packagingOptions {
exclude 'META-INF/notice.txt'
exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/license.txt'
exclude 'META-INF/services/javax.annotation.processing.Processor'
}
}
依賴aar
首先copy *.aar文件到對應的module的libs目錄.然后在對應的module的build.gradle文件中進行配置.
allprojects {
repositories {
jcenter()
flatDir {
dirs 'libs'
}
}
}
dependencies {
compile(name: 'sampleAar', ext: 'aar')
}
總結
暫時把我平時使用的配置風格都大概過了一下.雖然都只是簡單的介紹.但是基本都還算是完整.能夠滿足項目需求.如果有其他的疑問.歡迎留言.大家進行溝通交流.同時如果我上文中提及到的點有所疏漏.還有各位兄弟們提點一二.