上次咱們說到gradle的原理,主要是偏理論上的知識點,直通車在這Android Gradle系列-原理篇。此次咱們來點實戰的,隨便鞏固下以前的知識點。java
在app module下的gradle.build中都有一個android閉包,主要配置都在這裏設置。例如默認配置項:defaultConfig;簽名相關:signingConfig;構建變體:buildTypes;產品風格:productFlavors;源集配置:sourceSets等。android
對於defaultConfig其實它是也一個productFlavor,只不過這裏是用來提供默認的設置項,若是以後的productFlavor沒有特殊指定的配置都會使用defaultConfig中的默認配置。git
public class DefaultConfig extends BaseFlavor {
@Inject
public DefaultConfig(
@NonNull String name,
@NonNull Project project,
@NonNull ObjectFactory objectFactory,
@NonNull DeprecationReporter deprecationReporter,
@NonNull Logger logger) {
super(name, project, objectFactory, deprecationReporter, logger);
}
}
public abstract class BaseFlavor extends DefaultProductFlavor implements CoreProductFlavor {
...
}
複製代碼
能夠看到defaultConfig的超級父類就是DefaultProductFlavor。而在DefaultProductFlavor中定義了許多咱們常常見到的配置:VersionCode、VersionName、minSdkVersion、targetSdkVersion與applicationId等等。github
有了上面的基礎,那麼在defaultConfig中咱們要配置的變量就顯而易見了。api
defaultConfig {
applicationId "com.idisfkj.androidapianalysis"
minSdkVersion 16
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
複製代碼
signingConfig是用來配置keyStore,咱們能夠針對不一樣的版本配置不一樣的keyStore,例如安全
signingConfigs {
config { //默認配置
storeFile file('key.store')
storePassword 'android123'
keyAlias 'android'
keyPassword 'android123'
}
dev { //dev測試版配置
storeFile file('xxxx')
storePassword 'xxx'
keyAlias 'xxx'
keyPassword 'xxx'
}
}
複製代碼
有人可能會說這不安全的,密碼都是明文,都暴露出去了。是的,若是這項目發佈到遠程,那麼這些祕鑰就泄露出去了。因此爲了安全起見,咱們能夠對其進一些特殊處理。bash
storePassword System.getenv("KSTOREPWD")
keyPassword System.getenv("KEYPWD")
複製代碼
storePassword System.console().readLine("\nKeystore password: ")
keyPassword System.console().readLine("\nKey password: ")
複製代碼
上面兩種是Android Develop官網提供的,但通過測試都會報null異常,查了下資料都說是gradle不支持(若是有成功的能夠告知我),因此仍是推薦下面的這種方法網絡
在項目的根目錄下(settings.gradle平級)建立keystore.properties文件,咱們在這個文件中進行存儲祕鑰,它是支持key-value模式的鍵值對數據閉包
storePassword = android123
keyPassword = android123
複製代碼
以後就是讀取其中的password,在build.gradle經過afterEvaluate回調進行讀取與設置app
afterEvaluate {
def propsFile = rootProject.file('keystore.properties')
def configName = 'config'
if (propsFile.exists() && android.signingConfigs.hasProperty(configName)) {
def props = new Properties()
props.load(new FileInputStream(propsFile))
android.signingConfigs[configName].keyPassword = props['keyPassword']
android.signingConfigs[configName].storePassword = props['storePassword']
}
}
複製代碼
咱們已經經過動態讀取了password,因此在以前的signingConfigs中就無需再配置password
signingConfigs {
config {
storeFile file('key.store')
keyAlias 'android'
}
}
複製代碼
最後一步,爲了保證祕鑰的安全性,在.gitignore中添加keystore.properties的忽略配置,防止上傳到遠程倉儲暴露祕鑰。
構建變體主要用來配置shrinkResources:資源是否須要壓縮、zipAlignEnabled:壓縮是否對齊、minifyEnabled:是否代碼混淆與signingConfig:簽名配置等等。新建項目時,默認有一個release配置,但咱們實際開發中可能須要多個不一樣的配置,例如debug模式,爲了方法調試,通常都不須要對其進行代碼混淆、壓縮等處理。或者outer模式,須要的簽名配置不一樣,因此最終的配置能夠是這樣:
buildTypes {
debug {
minifyEnabled false
zipAlignEnabled false
shrinkResources false
signingConfig signingConfigs.config
}
outer {
minifyEnabled false
zipAlignEnabled false
shrinkResources false
signingConfig signingConfigs.outConfig
}
release {
minifyEnabled true
zipAlignEnabled true
shrinkResources true
signingConfig signingConfigs.config
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
複製代碼
Sync Now以後,打開Android Studio 右邊的Gradle,找到app->Tasks->build,發現已經添加了assembleDebug與assembleOuter構建task。
一個項目可能有不一樣的版本環境,例如開發功能中的開發版、項目上線的正式版。開發版與正式版請求的數據api可能不一樣,對於這種狀況咱們就可使用productFlavor來構建不一樣的產品風格,能夠看下面的dev與prod配置
flavorDimensions "mode"
productFlavors {
dev {
applicationIdSuffix ".dev"
dimension "mode"
manifestPlaceholders = [PROJECT_NAME: "@string/app_name_dev",
APP_ID : "21321843"]
buildConfigField 'String', 'API_URL', '"https://dev.idisfkj.android.com"'
buildConfigField 'String', 'APP_KEY', '"3824yk32"'
}
prod {
applicationIdSuffix ".prod"
dimension "mode"
manifestPlaceholders = [PROJECT_NAME: "@string/app_name",
APP_ID : "12932843"]
buildConfigField 'String', 'API_URL', '"https://prod.idisfkj.android.com"'
buildConfigField 'String', 'APP_KEY', '"32143dsk2"'
}
}
複製代碼
對於判斷是否爲同一個app,手機系統是根據app的applicationId來識別的,默認applicationId是packageName。因此爲了讓dev與prod的版本都能共存在一個手機上,能夠經過applicationIdSuffix來爲applicationId增長後綴,改變安裝包的惟一標識。
還有能夠經過manifestPlaceholders來配置可用於AndroidManifest中的變量,例如根據不一樣的產品風格顯示不一樣的app名稱
dev與prod網絡請求時使用不一樣的api host,能夠設置buildConfigField,這樣咱們就能夠在代碼中經過BuildConfig獲取
fun getApiUlr(): String {
return BuildConfig.API_URL
}
複製代碼
這裏的BuildConfig會根據你構建的產品風格返回不一樣的值,它位於build->generated->source->buildConfig->變體,大體內容以下:
public final class BuildConfig {
public static final boolean DEBUG = Boolean.parseBoolean("true");
public static final String APPLICATION_ID = "com.idisfkj.androidapianalysis.dev";
public static final String BUILD_TYPE = "debug";
public static final String FLAVOR = "devMinApi21";
public static final int VERSION_CODE = 20001;
public static final String VERSION_NAME = "1.0-minApi21";
public static final String FLAVOR_mode = "dev";
public static final String FLAVOR_api = "minApi21";
// Fields from product flavor: dev
public static final String API_URL = "https://dev.idisfkj.android.com";
public static final String APP_KEY = "3824yk32";
}
複製代碼
Sync Now以後,打開Android Studio 右邊的Gradle,找到app->Tasks->build,發現新添加了assembleDev與assembleProd構建task。
flavorDimensions是用來設置多維度的,上面的例子只展現了一個維度,因此dimension爲mode的形式。咱們新增一個api維度,構建不一樣的minSkdVerison版本的apk
flavorDimensions "mode", "api"
productFlavors {
dev {
applicationIdSuffix ".dev"
dimension "mode"
manifestPlaceholders = [PROJECT_NAME: "@string/app_name_dev",
APP_ID : "21321843"]
buildConfigField 'String', 'API_URL', '"https://dev.idisfkj.android.com"'
buildConfigField 'String', 'APP_KEY', '"3824yk32"'
}
prod {
applicationIdSuffix ".prod"
dimension "mode"
manifestPlaceholders = [PROJECT_NAME: "@string/app_name",
APP_ID : "12932843"]
buildConfigField 'String', 'API_URL', '"https://prod.idisfkj.android.com"'
buildConfigField 'String', 'APP_KEY', '"32143dsk2"'
}
minApi16 {
dimension "api"
minSdkVersion 16
versionCode 10000 + android.defaultConfig.versionCode
versionNameSuffix "-minApi16"
}
minApi21 {
dimension "api"
minSdkVersion 21
versionCode 20000 + android.defaultConfig.versionCode
versionNameSuffix "-minApi21"
}
}
複製代碼
gradle建立的構建變體數量等於每一個風格維度中的風格數量與你配置的構建類型數量的乘積,因此上面例子的構建變體數量爲12個。在gradle爲每一個構建變體或對應apk命名時,屬於較高優先級風格維度的產品風格首先顯示,以後是較低優先級維度的產品風格,再以後是構建類型。而優先級的判斷則以flavorDimensions的值順序爲依據,以上面的構建配置爲例:
構建變體:[dev, prod][minApi16, minApi21][debug, outer, release] 對應apk:app-[dev, prod]-[minApi16, minApi21]-[debug, outer, release].apk
構建變體有這麼多,但有時咱們並不所有須要,例如你不須要mode爲dev,api爲minApi16的變體,這時你就可使用variantFilter方法來過濾
variantFilter { variant ->
def names = variant.flavors*.name
if (names.contains("minApi16") && names.contains("dev")) {
setIgnore(true)
}
}
複製代碼
你再回到app->Tasks中查看變體,會發現已經將devMinApi16相關的變體過濾了。
你不只能夠過濾構建變體,還能夠改變默認的apk輸出名稱。例如你想修改buildType爲release的apk名稱,這時你可使用android.applicationVariants.all
android.applicationVariants.all { variant ->
if (variant.buildType.name == buildTypes.release.name) {
variant.outputs.all {
outputFileName = "analysis-release-${defaultConfig.versionName}.apk"
}
}
}
複製代碼
這樣在release下的包名都是以analysis打頭
Android Studio會幫助咱們建立默認的main源集與目錄(位於app/src/main),用來存儲全部構建變體間的共享資源。因此你能夠經過設置main源集來更改默認的配置。例如如今你想將res的路徑修改爲src/custom/res
sourceSets {
main {
res.srcDirs = ['src/custom/res']
}
}
複製代碼
這樣res資源路徑就定位到了src/custom/res下,固然你也能夠修改其它的配置,例如java、assets、jni等。
若是你配置了多個路徑,即路徑集合:
sourceSets {
main {
res.srcDirs = ['src/custom/res', 'scr/main/res']
}
}
複製代碼
這時你要保證不能有相同的名稱,即每一個文件只能惟一存在其中一個目錄下。
你也能夠查看因此的構建變體的默認配置路徑: 點擊右邊gradle->app->android->sourceSets,你將會看到以下相似信息
------------------------------------------------------------
Project :app
------------------------------------------------------------
androidTest
-----------
Compile configuration: androidTestCompile
build.gradle name: android.sourceSets.androidTest
Java sources: [app/src/androidTest/java]
Manifest file: app/src/androidTest/AndroidManifest.xml
Android resources: [app/src/androidTest/res]
Assets: [app/src/androidTest/assets]
AIDL sources: [app/src/androidTest/aidl]
RenderScript sources: [app/src/androidTest/rs]
JNI sources: [app/src/androidTest/jni]
JNI libraries: [app/src/androidTest/jniLibs]
Java-style resources: [app/src/androidTest/resources]
...
複製代碼
上面是androidTest變體的默認路徑,首先它會去查找相應的構建變體的默認位置,若是沒有找到,就會使用main源集下的默認配置。也就是咱們所熟悉的app/src/main路徑下的資源。
由於它是跟構建變體來搜索的,因此它有個優先級:
對於源集的建立,以下所示在app/src下右鍵新建,但它只會幫你建立源集下的java文件夾,其它的都要你本身逐個建立
咱們自定義一個debug源集,因此進去以後Target Source Set選擇debug,再點擊finish結束。這時你將會在src下看到debug文件夾
如今你已經有了debug的源集目錄,假設你如今要使debug下的app名稱展現成Android精華錄debug(默認是Android精華錄)。這時你能夠右鍵debug新建values
在values目錄下新建strings.xml,而後在其中配置app_name
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Android精華錄debug</string>
</resources>
複製代碼
最後你再去構建debug相關的變體時,你安裝的app展現的名稱將是Android精華錄debug。
因此經過修改mian源集或者配置其它的變體源集,能夠實現根據變體加載不一樣的數據源。這樣系統化的配置加載資源將更加方便項目測試與版本須要的配置。
dependencies閉包上用來配置項目的第三方依賴,若是你根據上面的配置有設置變體,那麼你將能夠根據變體來選擇性的依賴第三方庫
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
//根據變體選擇性依賴
outerImplementation '...'
prodMinApi21Implementation '...'
}
複製代碼
關於dependencies,這只是簡單的配置方式,以後我還會單獨抽出一篇文章來寫系統化的配置dependencies,感興趣的能夠關注下。
gradle相關的配置還有不少,這裏只是冰山一角,但個人建議是根據你的實際需求去學習與研究,相信你也會有意想不到的成長。
最後附上源碼地址:github.com/idisfkj/and…
博客地址:www.rousetime.com/