1

博客主页

在讲解Task之前,先了解Gradle核心之Project详解及实战

Project相关的API

获取Project

查看所有的Project,在Gradle中为我们提供了Task任务projects,执行下面命令后,会列出所有的Project

> gradlew projects

// 执行上面命令后,列出所有的project,包括Root project
------------------------------------------------------------
Root project
------------------------------------------------------------

Root project 'walletApp'
\--- Project ':app

我们可以通过Project提供的API访问所有的project。getAllprojects返回所有的project,包括当前project,返回类型:Set集合

def getMyAllProjects() {
    println "-------------------------------------"
    println "Root Project"
    println "-------------------------------------"

    // 获取所有project,包括本身
    this.getAllprojects().eachWithIndex { Project project, int index ->

        if (index == 0) {
            println "Root Project: ${project.name}"
        } else {
            println "+---- Project: ${project.name}"
        }
    }
}

也提供了getSubprojects返回所有子project,返回类型:Set集合

def getMySubprojects() {
    println "-------------------------------------"
    println "Sub Project"
    println "-------------------------------------"

    // 获取所有子project
    this.getSubprojects().eachWithIndex { Project project, int index ->
        println "+---- Project: ${project.name}"
    }
}

除了上面两种方式,还提供了获取 Parent projectRoot project ,对应的方法分别是getParent()getRootProject()。他们主要区别是,getParent()如果本身就是Root Project,则返回null;而getRootProject()如果本身就是Root Project,返回Root project,不会返回null

/**
 * 获取父Project, 如果本身就是Root Project,返回null
 */
def getMyParentProject() {
    // The parent project, or null if this is the root project.
    if (this.getParent()) {
        println "the parent project name is : ${this.getParent().name}"
    } else {
        println "the parent project is null"
    }
}

/**
 * 获取Root Project,如果本身就是Root Project,返回自己,不会返回null
 */
def getMyRootProject() {
    // The root project. Never returns null.
    def name = this.getRootProject().name
    println "the root project name is: ${name}"
}

统一配置Project

可以在Root project中通过Project提供的project方法对单个project进行独立配置,如应用插件、project分组、project版本、依赖等信息

project('app') { Project project ->
    project.apply plugin: 'com.android.application'
    project.group 'com.yqb.mm'
    project.version '1.0.0'

    project.dependencies {

    }
}

每个Project都会有一个Build文件,可以通过Project提供的subprojects 或者 allprojects 可以对Child Project统一配置

this.subprojects {
    println "The project name is ${project.name}"
}

// 输出日志信息
The project name is app
The project name is basiclib

属性相关的API

Project默认提供下面几种属性,从下图中可以看出,为什么gradle中build文件名是build.gradle了。

除了Project默认提供的,我们也可以通过ext关键字自定义属性。下面是我们自定义的应用包名、版本信息、依赖、签名文件等相关信息。

ext {
    applicationId = "com.kerwin.test"

    // android sdk version
    // 使用如下:
    // def versions = rootProject.ext.versions
    // compileSdkVersion versions.compileSdkVersion
    // buildToolsVersion versions.buildToolsVersion
    versions = [
            compileSdkVersion: 26,
            minSdkVersion    : 19,
            targetSdkVersion : 26,
            versionCode      : 182,
            versionName      : '1.8.2',
    ]

    // dependencies
    // 使用如下:
    // def dependencies = rootProject.ext.dependencies
    // compile dependencies.support.appcompat
    dependencies = [
            support     : [
                    appcompat : "com.android.support:appcompat-v7:26.1.0",
                    constraint: "com.android.support.constraint:constraint-layout:1.1.3",
                    design    : "com.android.support:design:26.1.0"
            ],
            gson        : "com.google.code.gson:gson:2.8.5"
    ]

    signingConfigs = [
            debug: [
                    storeFile    : '../keystore/mm_debug.keystore',
                    storePassword: 'pa123456',
                    keyAlias     : 'mm_key',
                    keyPassword  : 'pa123456',
            ]
    ]
}

除了上面方式自定义属性外,还可以在gradle.properties文件中定义,但只能是简单的Key-Value形式.
gradle.properties文件中自定义如下属性:

// gradle.properties
TINKER_ENABLE=true

例如可以在settings.gradle文件中根据在gradle.properties文件中定义的属性做一些操作

if (hasProperty('TINKER_ENABLE') ? Boolean.parseBoolean(TINKER_ENABLE) : false) {
    println "TINKER_ENABLE 打开了."
} else {
    println "TINKER_ENABLE 关闭了."
}

// 当TINKER_ENABLE=true,输出日志信息
TINKER_ENABLE 打开了.

文件相关API

路径获取相关API

在Project中提供了很多获取文件路径的方法,如:getProjectDir()getRootDir()getBuildDir()

// Root project的根目录路径
println "the root directory of this project, ${project.getRootDir().absolutePath}"
// 当前project的build文件路径
println "the build directory of this project, ${project.getBuildDir().absolutePath}"
// 当前project的目录路径,如果当前project是Root project,等同于getRootDir()
println "The directory containing the project build file, ${project.getProjectDir().absolutePath}"

// 输出的日志信息
the root directory of this project, D:\work\yqb.com\newCode\merchantApp
the build directory of this project, D:\work\yqb.com\newCode\merchantApp\build
The directory containing the project build file, D:\work\yqb.com\newCode\merchantApp

文件操作相关API

文件的定位,根据文件路径获取文件内容
println getContent('settings.gradle')

def getContent(String path) {
    try {
        def file = file(path)
        return file.text
    } catch (Exception ex) {
        println "getContent has error: ${ex.getMessage()}"
        return ""
    }
}
文件拷贝

Project为我们提供了简便的方法copy对文件进行拷贝。

def copyApk() {
    this.copy {
//        srcApkDir>>> D:\work\yqb.com\newCode\merchantApp\app\build\bakApk
//        destApkDir>>> D:\work\yqb.com\newCode\merchantApp\build\apk

        // from 用于指定拷贝的源文件或者文件夹
        from file("${buildDir}/bakApk/")
        
        // into 用于指定拷贝的目的地
        into file("${getRootProject().getBuildDir().path}/apk")
    }
}

除了使用frominto 指定源路径和目的地之外,还可以配置拷贝后使用rename文件重新命名、exclude移除不需拷贝的文件等。

def copyApk() {
    this.copy {
//        srcApkDir>>> D:\work\yqb.com\newCode\merchantApp\app\build\bakApk
//        destApkDir>>> D:\work\yqb.com\newCode\merchantApp\build\apk
        // 指定拷贝的源文件或者文件夹
        from file("${buildDir}/bakApk/")

        // 指定拷贝的目的地
        into file("${getRootProject().getBuildDir().path}/apk")

        // 移除不需要拷贝的文件, 例如:不拷贝以txt结尾的文件
        //exclude "**/*.txt"

        // 也可以使用闭包,移除不需要拷贝的文件
        exclude { details ->
            println "exclude>>> file: ${details.file}"
            return details.file.name.endsWith('.txt')
        }

        // 重新命名拷贝的文件名
        rename { String fileName ->
            println "rename>> fileName: ${fileName}"
            fileName.replace("app-arm-debug.apk", "test.apk")
        }
    }
}
文件树遍历

Project提供的fileTree方法,可以将指定文件目录下所有的文件封装成文件树对象操作

fileTree("build/outputs/apk") { ConfigurableFileTree fileTree ->
    fileTree.visit { FileVisitDetails details ->
        println "The file name is ${details.file}"

        copy {
            from details.file
            into "${getRootProject().getBuildDir().path}/apk"

            exclude { file ->
                return file.file.isDirectory()
            }
        }
    }
}

依赖相关API

Project提供了依赖相关的API,如 buildscript

buildscript {
    repositories {
        maven { url "http://maven.aliyun.com/nexus/content/groups/public/" }
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.0.1'
        classpath 'com.alibaba:arouter-register:1.0.2'
      
        classpath("com.tencent.tinker:tinker-patch-gradle-plugin:${TINKER_VERSION}") {
            changing = TINKER_VERSION?.endsWith("-SNAPSHOT")
            exclude group: 'com.android.tools.build', module: 'gradle'
        }
    }
}

执行外部命令API

可以使用Project提供的javaexec 或者 exec 执行一个外部命令。使用外部命令实现一个copy功能

tasks.create(name: 'copyAPK') {
    doLast {
        def srcFilePath = this.buildDir.path + "/outputs/apk"
        def destFilePath = this.buildDir.path + "/outputs/backup"
        def command = "mv -f ${srcFilePath} ${destFilePath}"

        exec { ExecSpec execSpec ->
            try {
                executable 'bash'
                args '-c', command
            } catch (Exception ex) {
                println "copyAPK>>> error: ${ex}"
            }
        }
    }
}

如果我的文章对您有帮助,不妨点个赞鼓励一下(^_^)


小兵兵同学
56 声望23 粉丝

Android技术分享平台,每个工作日都有优质技术文章分享。从技术角度,分享生活工作的点滴。