1. 程式人生 > 其它 >Gradle核心之Project

Gradle核心之Project

技術標籤:Gradleandroid

前言

project在gradle裡起到裡重要的作用,上節我們也說過可以通過./gradlew projects列印當前專案下所有的project,準確的說是有build.gradle的檔案既是一個project。而有多少project取決於在setting.gradle檔案中設定了多少個。一個project對應一個輸出,而具體輸出什麼取決於build.gradle裡面的內容。

Project核心API

每個工程下都有一個build.gradle檔案。根目錄的build.gradle可以管理子工程下面的build.gradle。下面具體看下project相關api。

getAllProjects()

我們可以通過getAllprojects打印出所有的project,可以理解成和./gradlew projects命令一樣。示例帶入如下:

getAllprojects().eachWithIndex { Project entry, int i ->
    if (i == 0) {
        println("RootProject-------${entry.name}")
    } else {
        println("SubProject-------${entry.name}")
    }
}

輸出結果如下:

RootProject-------APMProjetct
SubProject-------apm
SubProject-------app
SubProject-------aspectj

getSubprojects()

此方法是獲取所有子工程的例項,示例程式碼如下:

getSubprojects().eachWithIndex { Project entry, int i ->
        println("SubProject-------${entry.name}")
}

輸出結果如下:

> Configure project :
SubProject-------apm SubProject-------app SubProject-------aspectj

getRootProject()

既然我們可以拿到所有的子節點,那我們依然可以單獨獲取到根節點,示例程式碼如下:

println("the root project name is ${getRootProject().name}")

輸出結果如下:

> Configure project :
the root project name is APMProjetct

getParent()

此方法是用於在子工程獲取父工程的例項,示例程式碼如下:

println("the parent project name is ${getParent().name}")

輸出結果可想而知:

> Configure project :aspectj
the parent project name is APMProjetct

projects()

project是為了讓我們在父工程的build.gradle檔案,針對子project做一些單獨的處理,比如這樣:

project("app") {
    apply plugin: 'com.android.application'
}

allprojects()

allprojects 表示用於配置當前 project 及其每一個子 project,在 allprojects 中我們一般用來配置一些通用的配置,比如最常見的全域性倉庫配置。

allprojects {
    repositories {
        google()
        jcenter()
    }
}

subprojects()

此方法是為了讓我們對所有的子project做一些配置,比如這樣:

subprojects {
    if(project.plugins.hasPlugin("com.android.library")){
        apply from "../uploadMaven.gradle"
    }
}

ext擴充套件屬性

我們可以通過ext擴充套件屬性預設修改其他工程build.gradle的檔案配置。比如我在root project中新增瞭如下屬性:

ext {
    minSdkVersion = 21
    targetSdkVersion = 30
}

那麼我們可以在子工程的build.gralde檔案修改檔案如下:

android {
    compileSdkVersion 30
    buildToolsVersion "30.0.1"

    defaultConfig {
        applicationId "com.xxxxx.apmprojetct"
        minSdkVersion rootProject.ext.minSdkVersion
        targetSdkVersion rootProject.ext.targetSdkVersion
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
}

另外我們還可以通過apply的形式依賴自己新增的gradle檔案,比如我們可以新增一個config.gradle檔案,具體內容如下:

android {
    signingConfigs {
        debug {
            storeFile file('xxx.jks')
            storePassword 'xxx'
            keyAlias 'xxxx'
            keyPassword 'xxxx'
            v1SigningEnabled true
            v2SigningEnabled true
        }
        release {
            storeFile file('xxx.jks')
            storePassword 'xxx'
            keyAlias 'xxxx'
            keyPassword 'xxx'
            v1SigningEnabled true
            v2SigningEnabled true
        }
    }
}

那麼我們可以在工程中這樣使用:

apply from: 'config.gradle'
android {
      buildTypes {
        debug {
            signingConfig signingConfigs.debug
            minifyEnabled false
            shrinkResources false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            versionNameSuffix "_dev"
        }
        release {
            signingConfig signingConfigs.release
            minifyEnabled true
            shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

gradle檔案操作

gradle中操作本地檔案可以使用的是Project.file()方法,通過指定檔案的相對路徑或者絕對路徑來進行操作。比如我們可以新建一個檔案,可以檢視一個檔案的絕對路徑,也可以判斷一個檔案是否存在,示例程式碼如下:

// 使用相對路徑
File configFile = file('src/main/java/test.java')
configFile.createNewFile();

// 使用絕對路徑
configFile = file(/Users/xxx/xxx/src/main/java/test.java’)
println(configFile.absolutePath)

// 使用一個檔案物件
configFile = file(new File('src/main/java/test.java'))
// 列印檔案是否存在
println(configFile.exists())

同步一下,輸出結果如下:

/Users/xxx/xxx/src/main/java/test.java
true

檔案集合

Gradle中檔案集合就是類似於java中的陣列,Gradle中使用FileCollection介面表示,我們可以使用Project.files()方法來獲得一個檔案集合物件,所有檔案集合都是用到的時候才會建立。示例程式碼如下:

FileCollection collection = files('src/test1.txt',
        new File('src/test2.txt'),
        ['src/test3.txt', 'src/test4.txt'])

我們可以這樣遍歷檔案:

// 遍歷所有集合
collection.each { File file ->
    println file.name
}

我們還可以這樣轉換檔案:

// 把檔案集合轉換為Set型別
Set set1 = collection.files
Set set2 = collection as Set
// 把檔案集合轉換為List型別
List list = collection as List
// 把檔案集合轉換為String型別
String path = collection.asPath
// 把檔案集合轉換為File型別
File file1 = collection.singleFile
File file2 = collection as File

除此之外,我們還可以新增或者刪除一個檔案,比如這樣:

// 新增或者刪除一個集合
def union = collection + files('src/test5.txt')
def different = collection - files('src/test3.txt')

檔案樹

檔案樹你可以理解成是一個有層級結構的檔案集合,所以檔案樹中包含了所有檔案集合的操作。我們可以使用Project.fileTree()方法來建立檔案樹物件,還可以使用過慮條件來包含或排除相關檔案。示例程式碼如下:

// 指定目錄建立檔案樹物件
FileTree tree = fileTree(dir: 'src/main')

// 給檔案樹物件新增包含指定檔案
tree.include '**/*.java'
// 給檔案樹物件新增排除指定檔案
tree.exclude '**/Abstract*'

// 使用路徑建立檔案樹物件,同時指定包含的檔案
tree = fileTree('src').include('**/*.java')

// 通過閉包建立檔案樹
tree = fileTree('src') {
    include '**/*.java'
}

// 通過map建立檔案樹
tree = fileTree(dir: 'src', include: '**/*.java')
tree = fileTree(dir: 'src', includes: ['**/*.java', '**/*.xml'])
tree = fileTree(dir: 'src', include: '**/*.java', exclude: '**/*test*/**')

既然說到了檔案樹的建立,當然,我們也可以對檔案樹進行一些業務操作,比如:

// 遍歷檔案樹的所有檔案
tree.each {File file ->
    println file
}

// 過慮生成新的檔案樹物件
FileTree filtered = tree.matching {
    include 'org/gradle/api/**'
}

// 使用“+”號合併兩個檔案樹,同文件集合的“+”操作一樣
FileTree sum = tree + fileTree(dir: 'src/test')

// 訪問檔案樹中各項內容
tree.visit {element ->
    println "$element.relativePath => $element.file"
}

檔案拷貝

我們可以通過copy的task對檔案進行copy動作,並且可以指定copy內容和過濾copy內容,還可以在copy的過程中對檔案進行重新命名操作,先來個簡單的,比如我需要從A目錄copy到B目錄,那我們可以這樣做:

task copyTask(type: Copy) {
    from 'src/main/java/fileA'
    into 'src/main/kotlin/fileB'
}

當然,我們也是支援批量copy的,比如這樣:

task copyTask(type: Copy) {
    // 拷貝src/main/webapp目錄下所有的檔案
    from 'src/main/java'
    // 拷貝單獨的一個檔案
    from 'src/main/res/index.html'
    // 從Zip壓縮檔案中拷貝內容
    from zipTree('src/main/assets.zip')
    // 拷貝到的目標目錄
    into 'src/main/mouble'
}

前面我們也說了,我們可以在copy的時候進行重新命名,最常見的場景,就是專案拆解模組的時候,我們需要重新命名,那我們可以這樣操作:

task rename(type: Copy) {
    from 'moudleA/src/main/res/'
    into 'moudleB/src/main/res/'
    // 使用一個閉包方式重新命名檔案
    rename { String fileName ->
        fileName.replace('moudleA', 'moudleB')
    }
}

大量的節約了手動重新命名的成本。
同樣我們也可以新增過濾條件:

task copyTaskWithPatterns(type: Copy) {
    from 'src/main/drawable'
    into 'src/main/drawable-xxhdpi'
    include '*.jpg'
    exclude { details -> details.file.name.endsWith('.png') &&
                         details.file.text.contains('.webp') }
}

當然我們還可以拷貝的同時,並刪除對應目錄檔案,我們可以這麼操作:

task libs(type: Sync) {
    from configurations.runtime
    // 拷貝之前會把$buildDir/libs目錄下所有的清除
    into "$buildDir/libs"
}

總結

本文我們簡單的介紹了projects的相關操作以及在gradle中我們如何去操作檔案,當然,gradle中除了projects,還有一個核心的task,這個我們放在下一節進行介紹。

參考

暴力突破 Gradle 自動化專案構建(五)- Gradle 核心之 Project
gradle操作檔案詳解

本文首發於我的個人部落格:Gradle核心之Project

更多文章請關注我的公眾號:碼農職場
在這裡插入圖片描述