Gradle的神奇之處
Google I/O 2013大會上釋出了AS,如今已經發展到2.0-beta版本。相信已經大部分人做Android開發的都已經由Eclipse IDE轉為AS IDE。
本人從AS1.2版本開始使用,如今也用了大半年了。現將使用過程中的一些經驗分享給大家。
gradle的基本配置
apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "23.0.2"
defaultConfig {
applicationId "com.jacksen.multichannel"
minSdkVersion 14
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir : 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.1.1'
compile 'com.android.support:design:23.1.1'
}
上面的就是一個專案gradle的基本配置。
apply plugin: 'com.android.application'
表示該module是一個app module,應用了com.android.application外掛,也就是主程式。如果是一個第三方library,則應該是app plugin: ‘com.android.library’
buildTypes { } 表示構建型別。包括release和debug兩種。可以在這裡面配置啟用混淆、zipAlign、簽名信息等。
dependencies { } 裡面是專案的依賴資訊,包括jar包和第三方庫等資訊。
ApplicationId與PackageName的區別:
此前使用eclipse進行開發的時候,應用程式的包名是由manifest檔案的package屬性決定的:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.xxx.xxxx"
android:versionCode="1"
android:versionName="1.0" >
然而在使用gradle構建的時候卻多了一個applicationId。通常gradle中的applicationId和Manifest中的package是一樣的。其實可以使二者一致。
官方解釋說是:applicationId是你的應用在商店的唯一標識。而package是引用資源的路徑名,也就是R檔案的包名。
上面我們說到二者可以不一致,我們可以通過對不同的Flavor設定不同的applicationId,從而可以匯出不同“包名”的apk,而不需要修改其他的程式碼。後面會仔細說明。
gradle的簽名配置
我們通常執行專案都是使用debug的簽名。不過有些使用到第三方sdk的時候,需要用到正式版的簽名,通過打包正式簽名的方式又不好除錯。不過我們可以在gradle裡面配置正式版的簽名。
android {
signingConfigs {
config_release {
keyAlias 'releaseKey'
keyPassword '123456'
storePassword '123456'
storeFile file('key/releaseKey.jks')
}
config_debug {
keyAlias 'debugKey'
keyPassword '123456'
storePassword '123456'
storeFile file('key/debugKey.jks')
}
}
......//省略其他配置
}
這裡配置了兩個簽名。jsk都放在app下面的key資料夾中。所以使用的是相對路徑。
之前有人問我是怎麼知道keyAlias 、keyPassword這些語法的。其實在專案的【Project Structure】中都可以找到。
配置buildTypes
一般在buildTypes{ }裡面配置兩個(release和debug):
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
shrinkResources true //移除無效的resource檔案,必須允許ProGuard才能生效
zipAlignEnabled true
buildConfigField "boolean", "APP_TYPE", "true"
manifestPlaceholders = [APP_NAME: "@string/app_name_release"]
signingConfig signingConfigs.config
}
debug {
buildConfigField "boolean", "APP_TYPE", "false"
// manifestPlaceholders = [APP_NAME: "@string/app_name_debug"]
applicationIdSuffix 'debug'
}
}
這裡可配置的資訊很多,比如:是否可以是debug模式、簽名配置、混淆檔案等。
配置productFlavor
多渠道打包,關鍵就在於定義多個productFlavor。
在一個flavor裡面可配置的資訊很多。
productFlavors {
flavor_release {
buildConfigField "boolean", "APP_TYPE", "false"
applicationId 'com.jacksen.multichannel.release'
signingConfig signingConfigs.config_release
minSdkVersion 9
targetSdkVersion 15
versionCode 2
versionName '2.0.1'
}
flavor_debug {
minSdkVersion 10
applicationId 'com.jacksen.multichannel.debug'
signingConfig signingConfigs.config_debug
versionCode 5
versionName '5.0'
}
}
最主要的是可以在這裡配置applicationId,就是我們上面一開始說的打包多個不同“包名”的apk。
這樣有一個應用場景就是,我們一般通過debug版本進行測試之後,在進行release版本的測試。不過同一個applicationId的apk在一個測試機上只能存在一個。現在我們通過配置多個flavor對應多個applicationId,就可以在測試機上執行測試版和正式版兩個apk了。
productFlavors{ } 與 buildTypes{ }裡面的配置是多對多的關係。
比如:
buildTypes {
release {...}
debug {...}
}
productFlavors {
flavor_1 {...}
flavor_2 {...}
}
此時的配置可以打包出四個apk,分別是:
我們在使用as的打包工具時,可以選擇打包某一個Build type的某一個或多個Flavors:
如果在gradle裡面配置了簽名信息,那麼在【Generate Signed APK】的第一步填寫的簽名信息是以在gradle裡面配置並引用的為準。
我們還可以藉助gradlew命令來打包:
比如打包flavor_1對應的release和debug版本:
打包flavor_2對應的release版本:
實際上,我們不用再記住這些命令。AS裡面的gradle外掛就可以實現。
在AS的右側會有【Gradle】tab頁面。開啟之後,先重新整理一下。會看到如下圖示:
通過雙擊右邊的命令打包不同的版本。
除了build下面的命令,還有其他的命令,大家都可以嘗試一下,根據名字應該就能知道什麼意思,這裡不多介紹了。
manifest佔位符
在打包多個版本的時候,會遇到修改應用名稱等需求。不同的flavor有要求不通的名稱。此時可以在Manifest檔案中使用佔位符,然後在build.gradle中替換佔位符就行了。
首先定義佔位符:
application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="${APP_NAME}"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".LoginActivity"
android:label="${APP_NAME}">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
在build.gradle中替換:
buildTypes {
release {
manifestPlaceholders = [APP_NAME: "@string/app_name1"]
}
debug {
manifestPlaceholders = [APP_NAME: "@string/app_name1"]
}
}
productFlavors {
flavor_1 {
manifestPlaceholders = [APP_NAME: "@string/app_name1"]
}
flavor_2 {
manifestPlaceholders = [APP_NAME: "@string/app_name2"]
}
}
如果在productFlavors和buildTypes裡面都進行了替換,那麼是以productFlavors裡面的為準。
如果不區分productFlavors和buildTypes的話,也可以在defaultConfig裡進行替換:
defaultConfig {
manifestPlaceholders = [APP_NAME: "@string/app_name_release"]
}
其實defaultConfig也是productFlavors的一個子集。
新增自定義欄位
Gradle在generateSources階段為每一個flavor生成兩個BuildConfig.java檔案(對應在release和debug資料夾下)。
BuildConfig類預設會提供一些常量欄位。
public final class BuildConfig {
public static final boolean DEBUG = Boolean.parseBoolean("true");
public static final String APPLICATION_ID = "com.jacksen.multichannel";
public static final String BUILD_TYPE = "debug";
public static final String FLAVOR = "";
public static final int VERSION_CODE = 1;
public static final String VERSION_NAME = "1.0";
}
雖然通過上面的操作,我們可以同時打包出一個debug版本和一個release版本。但是我們還需要執行的時候有不同的表現。比如,release版本不顯示一些控制元件。
令人驚奇的是,我們可以通過buildConfigField在gradle裡面自定義一些欄位。
buildConfigField "String", "APP_TYPE", "debug"
我們可以檢視buildConfigField的原始碼:
/**
* Adds a new field to the generated BuildConfig class.
*
* <p>The field is generated as: <code><type> <name> = <value>;</code>
*
* <p>This means each of these must have valid Java content. If the type is a String, then the
* value should include quotes.
*
* @param type the type of the field
* @param name the name of the field
* @param value the value of the field
*/
public void buildConfigField(
@NonNull String type,
@NonNull String name,
@NonNull String value) {
ClassField alreadyPresent = getBuildConfigFields().get(name);
if (alreadyPresent != null) {
logger.info(
"BuildType(${getName()}): buildConfigField '$name' value is being replaced: ${alreadyPresent.value} -> $value");
}
addBuildConfigField(AndroidBuilder.createClassField(type, name, value));
}
註釋寫的很詳細。該方法就是新增一個field到BuildConfig類中。三個引數都是不可為空的。
新增完畢之後,就可以在程式碼中使用了:
textView.setText("BuildConfig.APP_TYPE : " + BuildConfig.APP_TYPE);
自定義匯出APK的名稱
我直接上程式碼吧。
/*applicationVariants.all {
variant ->
variant.outputs.each {
output ->
def outputFile = output.outputFile
if (outputFile != null && outputFile.name.endsWith('.apk')) {
// def fileName = outputFile.name.replace(".apk", "-${defaultConfig.versionName}.apk")
def apkType = ""
if (variant.flavorName.equals("releaseFlavor")){
apkType = "release"
}else if(variant.flavorName.equals("debugFlavor")){
apkType = "debug"
}
def fileName = new File(output.outputFile.getParent(), "app-" + apkType + "-${variant.versionName}.apk")
// output.outputFile = new File(outputFile.parent, fileName)
output.outputFile = fileName
}
}
}*/
applicationVariants.all {
variant ->
variant.outputs.each {
output ->
def outputFile = output.outputFile
if (outputFile != null && outputFile.name.endsWith('.apk')) {
def fileName = outputFile.name.replace(".apk", "-${defaultConfig.versionCode}-${defaultConfig.versionName}.apk")
output.outputFile = new File(outputFile.parent, fileName)
}
}
}
這裡有兩種方式,大家執行一下測試一下就明白了。
以上就是我對gradle的一些認識~~~
Demo下載
歡迎star~~
此篇blog到此結束~
感謝大家支援!如有錯誤,請指出~
謝謝~