1. 程式人生 > >Android Gradle使用總結

Android Gradle使用總結

其他

Android Gradle

Android專案使用 Gradle 作為構建框架,Gradle 又是以Groovy為指令碼語言。所以學習Gradle之前需要先熟悉Groovy指令碼語言。

Groovy是基於Java語言的指令碼語言,所以它的語法和Java非常相似,但是具有比java更好的靈活性。下面就列舉一些和Java的主要區別。

Android Gradle 的 Project 和 Tasks

這個Gradle中最重要的兩個概念。每次構建(build)至少由一個project構成,一個project 由一到多個task構成。專案結構中的每個build.gradle檔案代表一個project,在這編譯指令碼檔案中可以定義一系列的task;task 本質上又是由一組被順序執行的Action`物件構成,Action其實是一段程式碼塊,類似於Java中的方法。

Android Gradle 構建生命週期

每次構建的執行本質上執行一系列的Task。某些Task可能依賴其他Task。哪些沒有依賴的Task總會被最先執行,而且每個Task只會被執行一遍。每次構建的依賴關係是在構建的配置階段確定的。每次構建分為3個階段:

  • Initialization: 初始化階段

這是建立Project階段,構建工具根據每個build.gradle檔案創建出一個Project例項。初始化階段會執行專案根目錄下的settings.gradle檔案,來分析哪些專案參與構建。

所以這個檔案裡面的內容經常是:

include ':app'
include ':libraries:someProject'

這是告訴Gradle這些專案需要編譯,所以我們引入一些開源的專案的時候,需要在這裡填上對應的專案名稱,來告訴Gradle這些專案需要參與構建。

  • Configuration:配置階段

這個階段,通過執行構建指令碼來為每個project建立並配置Task。配置階段會去載入所有參與構建的專案的build.gradle檔案,會將每個build.gradle檔案例項化為一個Gradle的project物件。然後分析project之間的依賴關係,下載依賴檔案,分析project下的task之間的依賴關係。

  • Execution:執行階段

這是Task真正被執行的階段,Gradle會根據依賴關係決定哪些Task需要被執行,以及執行的先後順序。
task是Gradle中的最小執行單元,我們所有的構建,編譯,打包,debug,test等都是執行了某一個task,一個project可以有多個task,task之間可以互相依賴。例如我有兩個task,taskA和taskB,指定taskA依賴taskB,然後執行taskA,這時會先去執行taskB,taskB執行完畢後在執行taskA。

說到這可能會有疑問,我翻遍了build.gradle也沒看見一個task長啥樣,有一種被欺騙的趕腳!

其實不是,你點選AndroidStudio右側的一個Gradle按鈕,會開啟一個面板,內容差不多是這樣的:

這裡寫圖片描述

裡面的每一個條目都是一個task,那這些task是哪來的呢?

一個是根目錄下的 build.gradle 中的

dependencies {
        classpath 'com.android.tools.build:gradle:2.2.2'
    }

一個是 app 目錄下的 build.gradle 中的

apply plugin: 'com.android.application'

這兩段程式碼決定的。也就是說,Gradle提供了一個框架,這個框架有一些執行的機制可以讓你完成編譯,但是至於怎麼編譯是由外掛決定的。還好Google已經給我們寫好了Android對應的Gradle工具,我們使用就可以了。

根目錄下的build.gradle中dependencies {classpath ‘com.android.tools.build:gradle:2.2.2’}是Android Gradle編譯外掛的版本。

app目錄下的build.gradle中的apply plugin: ‘com.android.application’是引入了Android的應用構建專案,還有com.android.library和com.android.test用來構建library和測試。

所有Android構建需要執行的task都封裝在工具裡,如果你有一些特殊需求的話,也可以自己寫一些task。那麼對於開發一個Android應用來說,最關鍵的部分就是如何來用AndroidGradle的外掛了。

認知Gradle Wrapper

Android Studio中預設會使用 Gradle Wrapper 而不是直接使用Gradle。命令也是使用gradlew而不是gradle。這是因為gradle針對特定的開發環境的構建指令碼,新的gradle可能不能相容舊版的構建環境。為了解決這個問題,使用Gradle Wrapper 來間接使用 gradle。相當於在外邊包裹了一箇中間層。對開發者來說,直接使用Gradlew 即可,不需要關心 gradle的版本變化。Gradle Wrapper 會負責下載合適的的gradle版本來構建專案。

Android 三個檔案重要的 gradle 檔案

Gradle專案有3個重要的檔案需要深入理解:專案根目錄的 build.gradle , settings.gradle 和模組目錄的 build.gradle 。

  • 1.settings.gradle 檔案會在構建的 initialization 階段被執行,它用於告訴構建系統哪些模組需要包含到構建過程中。對於單模組專案, settings.gradle 檔案不是必需的。對於多模組專案,如果沒有該檔案,構建系統就不能知道該用到哪些模組。

  • 2.專案根目錄的 build.gradle 檔案用來配置針對所有模組的一些屬性。它預設包含2個程式碼塊:buildscript{…}和allprojects{…}。前者用於配置構建指令碼所用到的程式碼庫和依賴關係,後者用於定義所有模組需要用到的一些公共屬性。

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.3.2'
    }
}

allprojects {
    repositories {
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

buildscript:定義了 Android 編譯工具的類路徑。repositories中, jCenter是一個著名的 Maven 倉庫。

allprojects:中定義的屬性會被應用到所有 moudle 中,但是為了保證每個專案的獨立性,我們一般不會在這裡面操作太多共有的東西。

  • 3.模組級配置檔案 build.gradle 針對每個moudle 的配置,如果這裡的定義的選項和頂層 build.gradle定義的相同。它有3個重要的程式碼塊:plugin,android 和 dependencies。

定製專案屬性(project properties)

在專案根目錄的build.gradle配置檔案中,我們可以定製適用於所有模組的屬性,通過ext 程式碼塊來實現。如下所示:

ext {
    compileSdkVersion = 22
    buildToolsVersion = "22.0.1"
}

然後我們可以在模組目錄的build.gradle配置檔案中引用這些屬性,引用語法為rootProject.ext.{屬性名}。如下:

android {
    compileSdkVersion rootProject.ext.compileSdkVersion
    buildToolsVersion rootProject.ext.buildToolsVersion
}

Android studio gradle Task

這裡寫圖片描述

//構建
gradlew app:clean    //移除所有的編譯輸出檔案,比如apk

gradlew app:build   //構建 app module ,構建任務,相當於同時執行了check任務和assemble任務

//檢測
gradlew app:check   //執行lint檢測編譯。

//打包
gradlew app:assemble //可以編譯出release包和debug包,可以使用gradlew assembleRelease或者gradlew assembleDebug來單獨編譯一種包

gradlew app:assembleRelease  //app module 打 release 包

gradlew app:assembleDebug  //app module 打 debug 包

//安裝,解除安裝

gradlew app:installDebug  //安裝 app 的 debug 包到手機上

gradlew app:uninstallDebug  //解除安裝手機上 app 的 debug 包

gradlew app:uninstallRelease  //解除安裝手機上 app 的 release 包

gradlew app:uninstallAll  //解除安裝手機上所有 app 的包

這些都是基本的命令,在實際專案中會根據不同的配置,會對這些task 設定不同的依賴。比如 預設的 assmeble 會依賴 assembleDebug 和assembleRelease,如果直接執行assmeble,最後會編譯debug,和release 的所有版本出來。如果我們只需要編譯debug 版本,我們可以執行assembleDebug。

除此之外還有一些常用的新增的其他命令,比如 install命令,會將編譯後的apk 安裝到連線的裝置。

lint 檢測

  • 忽略編譯器的 lint 檢查
android {

  lintOptions {
      abortOnError false
  }

}

buildTypes 定義了編譯型別

android{

  buildTypes {
        release {
            minifyEnabled true  //開啟混淆
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
        debug {
            minifyEnabled false //關閉混淆
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
 }

productFlavors 多渠道打包

AndroidManifest.xml 裡設定動態渠道變數

<meta-data
    android:name="UMENG_CHANNEL"
    android:value="${UMENG_CHANNEL_VALUE}" />

在 build.gradle 設定 productFlavors , 這裡假定我們需要打包的渠道為酷安市場、360、小米、百度、豌豆莢。


android {  

    productFlavors {
        kuan {
            manifestPlaceholders = [UMENG_CHANNEL_VALUE: "kuan"]
        }
        xiaomi {
            manifestPlaceholders = [UMENG_CHANNEL_VALUE: "xiaomi"]
        }
        qh360 {
            manifestPlaceholders = [UMENG_CHANNEL_VALUE: "qh360"]
        }
        baidu {
            manifestPlaceholders = [UMENG_CHANNEL_VALUE: "baidu"]
        }
        wandoujia {
            manifestPlaceholders = [UMENG_CHANNEL_VALUE: "wandoujia"]
        }
    } 

}

或者批量修改

android {  

    productFlavors {
        kuan {}
        xiaomi {}
        qh360 {}
        baidu {}
        wandoujia {}
    }  

    productFlavors.all { 
        flavor -> flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name] 
    }
}

這樣在打包的時候就可以選擇渠道了

這裡寫圖片描述

或者用命令打包 ,比如:

gradlew assembleWandoujiaRelease  //豌豆莢 release 包

gradlew assembleWandoujiaDebug //豌豆莢 debug 包

多渠道設定包名

有時候我們需要分渠道設定 applicationId 、友盟的 appkey 、友盟渠道號。

    productFlavors {
        google {
            applicationId "com.wifi.cool"
            manifestPlaceholders = [                
                    UMENG_APPKEY_VALUE : "456789456789",
                    UMENG_CHANNEL_VALUE: "google",            
            ]
        }

        baidu{
            applicationId 'com.wifi.hacker'
            manifestPlaceholders = [
                    UMENG_APPKEY_VALUE     : "123456789789",
                    UMENG_CHANNEL_VALUE    : "baidu",          
            ]
        }
    }

Signing 簽名

在 android 標籤下新增 signingConfigs 標籤,如下:

android {
    signingConfigs {
        config {
            keyAlias 'yiba'
            keyPassword '123456'
            storeFile file('C:/work/Key.jks')
            storePassword '1234567'
        }
    }
 }   

可以在 release 和 debug 包中定義簽名,如下:

android {
    signingConfigs {
        config {
            keyAlias 'yiba'
            keyPassword '123456'
            storeFile file('C:/work/Key.jks')
            storePassword '1234567'
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.config
        }
        debug {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.config
        }
    }
}

依賴管理

1、依賴 jcenter 包

每個庫名稱包含三個元素:組名:庫名稱:版本號

 compile 'com.android.support:appcompat-v7:25.0.0'

2、依賴本地 module

 compile project(':YibaAnalytics')

3、依賴 jar 包

  • 1、把 jar 包放在 libs 目錄下
  • 2、在 build.gradle 中新增依賴
dependencies {
   compile files('libs/YibaAnalytics5.jar')
}

這裡寫圖片描述

4、依賴 aar 包

  • 1、把 aar 包放到 libs 目錄下
  • 2、在 build.gradle 中新增依賴
repositories {
    flatDir {
        dirs 'libs'
    }
}

dependencies {
    compile(name:'YibaAnalytics-release', ext:'aar')
}

如圖所示:

這裡寫圖片描述

5、自定義依賴包目錄

當我們的 aar 包需要被多個 module 依賴時,我們就不能把 aar 包放在單一的 module 中,我們可以在專案的根目錄建立一個目錄,比如叫 aar 目錄,然後把我們的 aar 包放進去,如圖所示:

這裡寫圖片描述

在專案的根目錄的 build.gradle 的 allprojects 標籤下的 repositories 新增 :

 flatDir {
     dirs '../aar'
}

../aar 表示根目錄下的 aar 資料夾。

如圖所示:

這裡寫圖片描述

然後就可以新增依賴了,如下所示:

 compile(name:'YibaAnalytics-release', ext:'aar')

6、依賴配置

有些時候,你可能需要和sdk協調工作。為了能順利編譯你的程式碼,你需要新增SDK到你的編譯環境。你不需要將sdk包含在你的APK中,因為它早已經存在於裝置中,所以配置來啦,我們會有5個不同的配置:

  • compile
  • apk
  • provided
  • testCompile
  • androidTestCompile

compile是預設的那個,其含義是包含所有的依賴包,即在APK裡,compile的依賴會存在。

apk的意思是apk中存在,但是不會加入編譯中,這個貌似用的比較少。

provided的意思是提供編譯支援,但是不會寫入apk。

7、排除依賴相容包

有時候我們在引入依賴包的時候,會額外的引入 v7, v4 的包,對我們的專案造成額外的負擔,我們需要把這個相容包排除,可以使用 exclude 就能做到。

compile('com.google.firebase:firebase-ads:11.0.4', {
      exclude group: 'com.android.support'   //排除v7 , v4 包
})

native包(so包)

用c或者c++寫的library會被叫做so包,Android外掛預設情況下支援native包,你需要把.so檔案放在對應的資料夾中:

這裡寫圖片描述

注意

jniLibs 目錄應該和 Java 目錄在同一級

defaultConfig 詳解

defaultConfig 對應的是 ProductFlavor 類。

resConfigs : 過濾語言

如果你的app中僅支援1,2種語言,但是可能引用的lib庫包含多種其他語言的strings資源,這個時候我們可以通過resConfig指定我們需要的strings資源。

android {

    defaultConfig {
        applicationId "com.yiba.sharewe.lite.activity"
        minSdkVersion 14
        targetSdkVersion 24
        versionCode 46
        versionName "1.74"
        resConfigs 'en', 'zh-rCN' ,'es'  //本次打包,只把 en(英文)、zh-rCN(中文簡體)、es(西班牙語)打進保內,其他語言忽略
    }
}

resConfigs : 過濾 drawable資料夾的資源

一般情況下,我們打完包,res 下面的資源如圖所示:

這裡寫圖片描述

現在加上資源過濾規則:

android {

    defaultConfig {
        applicationId "com.wifi.analytics"
        minSdkVersion 9
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        resConfigs "hdpi"  //打包的時候只保留 drawable-xhdpi 資料夾裡面的資源
    }

}

這次我們打包效果如下:

這裡寫圖片描述

buildTypes 詳解

官方文件

buildTypes{}對應的是 BuildType 類

繼承關係

BuildType 繼承 DefaultBuildType ; DefaultBuildType 繼承 BaseConfigImpl ;

BaseConfigImpl
    --- DefaultBuildType 
          --- BuildType

buildTypes的屬性:

name:build type的名字

applicationIdSuffix:應用id字尾

versionNameSuffix:版本名稱字尾

debuggable:是否生成一個debug的apk

minifyEnabled:是否混淆

proguardFiles:混淆檔案

signingConfig:簽名配置

manifestPlaceholders:清單佔位符

shrinkResources:是否去除未利用的資源,預設false,表示不去除。

zipAlignEnable:是否使用zipalign工具壓縮。

multiDexEnabled:是否拆成多個Dex

multiDexKeepFile:指定文字檔案編譯進主Dex檔案中

multiDexKeepProguard:指定混淆檔案編譯進主Dex檔案中

buildType的方法:

1.buildConfigField(type,name,value):新增一個變數生成BuildConfig類。

2.consumeProguardFile(proguardFile):新增一個混淆檔案進arr包。

3.consumeProguardFile(proguardFiles):新增混淆檔案進arr包。

4.externalNativeBuild(action):配置本地的build選項。

5.initWith:複製這個build型別的所有屬性。

6.proguardFile(proguardFile):新增一個新的混淆配置檔案。

7.proguradFiles(files):新增新的混淆檔案

8.resValue(type,name,value):新增一個新的生成資源

9.setProguardFiles(proguardFileIterable):設定一個混淆配置檔案。

initWith :複製屬性

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.2"
    defaultConfig {
        applicationId "com.wifi.analytics"
        minSdkVersion 9
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
        debug {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }

        myType {
            initWith debug  //完全複製 debug 的所有屬性‘
            minifyEnabled true //自定義開啟混淆
        }
    }
}

applicationIdSuffix 、versionNameSuffix :新增字尾

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.2"
    defaultConfig {
        applicationId "com.wifi.analytics"
        minSdkVersion 9
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
        debug {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            applicationIdSuffix "zhao"  //applicationId 追加字尾名 zhao
            versionNameSuffix "debug"  //versionName 追加字尾名 debug1.0
        }
    }

效果圖,如下:

這裡寫圖片描述

buildConfigField: 自定義屬性

在 build.gradle 檔案中定義 buildConfigField 屬性

android {

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            buildConfigField "String", "API_ENV", "\"http://yiba.com\""  //自定義String屬性
        }
        debug {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            buildConfigField "String", "API_ENV", "\"http://yiba.com\""  //自定義String屬性
        }
    }
}

然後點選同步按鈕,然後就可以在 build 目錄看到 debug 和 release 資訊。

debug 環境下的 BuildConfig 如下:
這裡寫圖片描述

release 環境下的 BuildConfig 如下:

這裡寫圖片描述

當然我們也可以在程式碼中獲取自定義的值:

//獲取變數值
String API = BuildConfig.API_ENV ;

上面演示了自定義 String 變數,也可以 自定義 int 、boolean

android {

    buildTypes {
        debug {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            buildConfigField "String", "API_ENV", "\"http://www.baidu.com\"" //自定義 String 值
            buildConfigField "Boolean", "openLog", "true" //自定義 boolean 值
            buildConfigField "int", "age", "10"   //自定義 int 值
        }
    }
}

Gradle 實現差異化構建

情景1

LeakCanary 是 square 公司出品的一個檢測記憶體洩漏的開源庫。

我們一般這樣整合

dependencies {
    compile 'com.squareup.leakcanary:leakcanary-android:1.5.2'
}

然後我們在 Application 類中初始化:

public class MyApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();

        LeakCanary.install(this);
    }
}

但是這樣整合有一個弊端,就是 debug 和 release 包都會把 LeakCanary 的原始碼打進去,如果我們在 release 包中不把 LeakCanary 原始碼打進去,怎麼辦? 還好 LeakCanary 給我們提供了一個方法,方法如下:

dependencies {

 //打 debug 包
 debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5.1'

 //打 release 包
 releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'

}

leakcanary-android-no-op 是一個空殼,裡面有2個空類,所以就可以避免把 LeakCanary 原始碼打進 release 包。但是這種方式有個缺陷,如果一些開源庫沒有提供 releaseCompile 庫,那我們改怎麼辦了?下面的情景2 就會講到解決方案。

情景2

Stetho 是 Faceboo k開源的Andorid除錯工具。當你的應用整合Stetho時,開發者可以訪問Chrome,在Chrome Developer Tools中檢視應用佈局,網路請求,sqlite,preference 等等。

從官網可以看到 stetho 沒有提供 releaseCompile 包 , 情景1 的方案就不能用了。新的思路整合方案如下:

dependencies {
    debugCompile 'com.facebook.stetho:stetho:1.5.0'
}

在 src 目錄下建立 debug 目錄、release 目錄 ,然後分別在 debug 目錄 和 release 目錄 建立 java 目錄 , 在 java 目錄中建立包名,比如: com.app , 如下圖所示:

這裡寫圖片描述

debug 目錄下建立 SDKManage 類 ,如下 :

public class SDKManager {

    public static void init(Context context) {
        //初始化 Stetho
        Stetho.initializeWithDefaults(context);
    }
}

release 目錄下建立 SDKManage 類 ,如下 :

public class SDKManager {

    public static void init(Context context) { 
        //這是一個空方法,目的是不引入 Stetho 原始碼
    }

}

在住專案中的 MyApplication 類,並且完成 Stetho 的初始化,如下:

public class MyApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        SDKManager.init(this);
    }
}

這樣我們便完成了簡單的差異化構建, 打出來的 release 包就沒有 Stetho 原始碼。

SourceSet

SourceSet 簡介

SourceSet 可以定義專案結構,也可以修改專案結構。Java外掛預設實現了兩個SourceSet,main 和 test。每個 SourceSet 都提供了一系列的屬性,通過這些屬性,可以定義該 SourceSet 所包含的原始檔。比如,java.srcDirs,resources.srcDirs 。Java 外掛中定義的其他任務,就根據 main 和 test 的這兩個 SourceSet 的定義來尋找產品程式碼和測試程式碼等。

SourceSet 定義原始碼目錄

在 Android 專案中,我們可以在 src/main/java 目錄新建 Java 檔案,如下圖所示:

這裡寫圖片描述

現在我們在 src 目錄下,新建 test1 目錄 ,發現不能在 test1 目錄中新建 Java 檔案,如下圖所示:

這裡寫圖片描述

為什麼在 test1 目錄不能新建 Java 檔案,因為 Gradle 中 SourceSet 預設定義的原始碼檔案路徑是src/main/java , 其他的檔案下下面的原始碼我們自然無法訪問。解決這個問題也很簡單,我們需要在 SourceSet 中增加一個原始碼路徑即可。如下所示:

android {

    sourceSets {
        main {
            java {
                srcDir 'src/test1' //指定原始碼目錄
            }
        }
    }
}

然後同步一下,就可以在 test1 目錄中新建 Java 檔案了。如下圖所示:

這裡寫圖片描述

當然我們也可以同時指定多個原始碼目錄,比如同時指定 test1 , test2 , test3 為原始碼目錄。

android {

    sourceSets {
        main {
            java {
                srcDir 'src/test1' //指定 test1 為原始碼目錄
                srcDir 'src/test2' //指定 test2 為原始碼目錄
                srcDir 'src/test3' //指定 test3 為原始碼目錄
            }
        }
    }
}

或者 這樣寫 :

android {
    sourceSets {
        main {
            java.srcDirs( 'src/test1' , 'src/test2' ,'src/test3' )
        }
    }
}

效果如下圖所示:

這裡寫圖片描述

SourceSet 定義資源目錄

定義 test1 目錄 Java 原始碼路徑、res 資源目錄。目錄結構如下圖所示:

這裡寫圖片描述

android {

    sourceSets {
        main {
            java.srcDirs('src/test1/java')  //定義java 原始碼
            res.srcDirs('src/test1/res')    //定義資源目錄(layout , drawable,values)
        }
    }
}

SourceSet 實現 layout 分包

對於一個大專案來說,頁面太多,佈局檔案就很多,有時在眾多佈局檔案中找某個模組的佈局檔案,很是痛苦,為了解決這個問題,我們可以在建立多個 layout 目錄,不同模組的佈局檔案放在不同的 layout 目錄中,這樣查詢起來,就容易很多。

例子:

比如我們的專案中,有兩個模組分別為:登入、註冊。

  • 第一步:把專案中 layout 資料夾改名字為 layouts

  • 第二步:在 layouts 目錄下,分別建立 login 、register 目錄 。

  • 第三步:分別在 login 、register 目錄下建立 layout 目錄。注意這一步是必須的,否則會報錯。

  • 第四步:把 登入佈局檔案、註冊佈局檔案 分別放在 第三步建立的對應的 layout 目錄下。

效果圖如下:

這裡寫圖片描述

SourceSet 實現如下:

android {

    sourceSets {
        main {
            res.srcDirs 'src/main/res/layouts/login'  //定義登入佈局目錄
            res.srcDirs 'src/main/res/layouts/register'  //定義註冊佈局目錄
        }
    }
}

SourceSet 定義 AndroidManifest 檔案

指定 test1 目錄下的 AndroidManifest 檔案。專案結構如下圖所示:

這裡寫圖片描述

程式碼如下:

android {

    sourceSets {
        main {
            manifest.srcFile 'src/test1/AndroidManifest.xml'
        }
    }
}

在元件化開發中, 我們需要針對 debug 與 release 模式下, 指定不同的 Manifest 檔案, 程式碼如下:

android {
    def appDebug = false;

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            appDebug = false;
        }

        debug {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            appDebug = false;
        }
    }

    sourceSets {
        main {
            if (appDebug) {
                manifest.srcFile 'src/test1/AndroidManifest.xml'
            } else {
                manifest.srcFile 'src/main/AndroidManifest.xml'
            }
        }
    }
}

SourceSet 定義 assets 目錄

Android Studio 專案目錄中,assets 預設目錄如下:

這裡寫圖片描述

如何重新定義 assets 目錄 。在專案的根目錄下建立 assets 目錄,如下圖所示:

這裡寫圖片描述

sourceSets 定義程式碼如下:

android {

    sourceSets {
        main {
            assets.srcDirs = ['assets']
        }
    }
}

SourceSet 定義其他資源


android {

    sourceSets {
        main {
            jniLibs.srcDirs  //定義 jni 目錄
            aidl.srcDirs  //定義 aidl 目錄
        }
    }
}

applicationVariants

定義 versionName 、VersionCode

在打包的時候分 debug 、release 版本 , 需要控制 versionName

android {

     applicationVariants.all { variant ->
        def flavor = variant.mergedFlavor
        def versionName = flavor.versionName
        if (variant.buildType.isDebuggable()) {
            versionName += "_debug"  //debug 名字
        } else {
            versionName += "_release" //release 名字
        }
        flavor.versionName = versionName
    }

}

定義 APK 包的名字

apply plugin: 'com.android.application'

android {

    defaultConfig {
        applicationId "android.plugin"
        minSdkVersion 14
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
        debug {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

    //定義渠道
    productFlavors {
        xiaomi {
            //小米
        }
        wandoujia {
            // 豌豆莢
        }
    }

    //打包命名
    applicationVariants.all {
        variant ->
            variant.outputs.each {
                output ->

                    //定義一個新的apk 名字。
                    // 新名字 = app 名字+ 渠道號 + 構建型別 + 版本號 + 當前構建時間
                    def apkName = "appName_${variant.flavorName}_${buildType.name}_v${variant.versionName}_${getTime()}.apk";
                    output.outputFile = new File(output.outputFile.parent, apkName);
            }
    }

}

//獲取當前時間
def getTime() {
    String today = new Date().format('YY年MM月dd日HH時mm分')
    return today
}

效果圖如下:

這裡寫圖片描述

Task

定義 task

//定義任務1
task task1<<{
    println 'task1'
}

//定義任務2
task task2<<{
    println 'task2'
}

mustRunAfter 定義 task 執行順序

//task2 的執行順訊在 task1 之後
task2.mustRunAfter task1
  • 測試1 : gradlew task1

效果如下:

:app:task1
task1
  • 測試2 : gradlew task2

效果如下:

:app:task2
task2
  • 測試3 : gradlew task1 task2

效果如下:

:app:task1
task1
:app:task2
task2
  • 測試4 : gradlew task2 task1

效果如下:

:app:task1
task1
:app:task2
task2

結論

如果單獨執行 task1 就只會執行 task1 的任務;單獨執行 task2 就只會執行 task2 的任務;
如果同時執行 task1、task2 , 一定會先執行 task1 , 等 task1 執行完後,就會執行 task2 內容。

擴充套件

上面 mustRunAfter 我們還有一種寫法,如下圖所示:

task2 {}.mustRunAfter task1

這個寫法的效果和 mustRunAfter 是一樣的,當然我們還可以在 花括號裡面寫一些任務,比如 :

task2 {
    println '我最先執行'
}.mustRunAfter task1

下面做個測試,測試命令如下:

gradlew task2 task1

效果如下:

我最先執行

:app:task1
task1
:app:task2
task2

dependsOn 定義 task 依賴

task2 任務依賴於 task1 ,執行 task2 就會先執行 task1

task2.dependsOn task1

測試:

gradlew task2

效果如下:

:app:task1
task1
:app:task2
task2

常用 Gradlew 命令

  • 1、gradlew -v : 檢視版本號
------------------------------------------------------------
Gradle 3.3
------------------------------------------------------------

Build time:   2017-01-03 15:31:04 UTC
Revision:     075893a3d0798c0c1f322899b41ceca82e4e134b

Groovy:       2.4.7
Ant:          Apache Ant(TM) version 1.9.6 compiled on June 29 2015
JVM:          1.8.0_112 (Oracle Corporation 25.112-b15)
OS:           Windows 10 10.0 amd64

  • 2、gradlew task : 檢視所有的 task

Gradle 日誌管理

Log 等級

除了常用的 debug , info , warning , error ,gradle 自己特有的 quiet 和l ifecycle 。

級別 用途
ERROR 錯誤資訊
QUIET 重要資訊
WARNING 警告資訊
LIFECYCLE 警告資訊
INFO 警告資訊
DEBUG 除錯資訊
  • 小例子

build.gradle

task hello << {
    logger.quiet("重要資訊")
    logger.info("資訊")
    logger.debug("除錯資訊")
    logger.lifecycle("過程資訊")

    //自定義資訊等級
    logger.log(LogLevel.ERROR, "錯誤資訊")

}

改變預設輸出

println 後跟資訊,gradle 會將其重定向到日誌系統中,預設為 quiet 等級。

當然你可以使用logger屬性來編寫不同等級的。

  • 舉個栗子

build.gradle

task hello <<{

    println( "我是一條普通的輸出")

    //改變預設的標準輸出為error級別
    logging.captureStandardOutput(LogLevel.ERROR)

    println( "我是一條不普通的輸出")
}

效果如下:

這裡寫圖片描述

參考資料

個人微訊號:zhaoyanjun125 , 歡迎關注