Android Gradle使用總結


轉載請標明出處:http://blog.csdn.net/zhaoyanjun6/article/details/77678577
本文出自【趙彥軍的博客】

其他

Groovy 使用完全解析 http://blog.csdn.net/zhaoyanjun6/article/details/70313790

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 包

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。

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 公司出品的一個檢測內存泄漏的開源庫。

GitHub : https://github.com/square/leakcanary

我們一般這樣集成

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 等等。

官網:http://facebook.github.io/stetho/

從官網可以看到 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

參考資料

Android 利用Gradle實現差異化構建

楊海 Android目錄結構


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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM