Gradle核心:Project

慢慢深入发现之前安卓gradle相关的的迷惑慢慢都解开了,慢慢的对gradle有所了解啦!!!

知识点

在这里插入图片描述

一、深入了解 Project

1、Project介绍

1、类似安卓的activity,gradle程序的入口,十分重要。
2、gradle中工程下的module其实也是project,为根工程的子project(执行命令gradle projects可以看出)
3、每个peoject必对应一个build.gradle文件,删除这个文件后,project就是普通的文件夹了。

2、模型:

project的模型就是树结构模型,一般只有两层。(一般不建议在子project中再建立project)

3、project作用

1、根project 唯一的作用管理子project(通过一系列api)
2、子project作用就是输出相应文件。

  • 比如安卓模块的app project就是输出apk的(apply plugin: ‘com.android.application’)
  • 安卓模块的library常用来输出aar(com.android.library)
  • java library 就是输出jar包的

注意:输出的内容由相应module下的build.gradle 来控制,每个peoject必对应一个build.gradle文件。

二、Project涉及知识点简介

在这里插入图片描述

1、project相关api

提供根project操作子project,子project操作根project的能力。

2、Task相关api

让project可以新增Task、使用已有Task的能力。

3、 属性api

gradle提供弄了一些属性,为project添加额外属性的能力。

4、file相关api

操作当前project下的文件

5、其他api

project加依赖、配置等。

三、详解:project相关api

科普
1、每个groovy脚本都会被编译器编译为script字节码
2、 每一个build.gradle 都会被编译为project的字节码。(build.gradle 就对应一个project)
3、build.gradle中写的逻辑其实就是相当于在project类中编写逻辑。
4、所有的build.gradle 的内容(方法、代码) 都是在执行在gradle的==配置阶段(Configuration)==的。
5、相应的build.gradle文件下的this就代表当前的Project对象。
6、project对象有个name属性,可以获得project的值

1、根project子project对象的获取

随便找个build.gradle 文件写逻辑即可。写完逻辑随便执行个简单的clean 任务。我们的build.gradle文件一定会被执行的。

//1  Set<Project>   获得当前工程下的所有project对象
this.getAllprojects().each {
    
    
    Project->project
    println(project.name)
}

//2 Set<Project>   获得当前工程下的所有子project
this.getSubprojects()

// 3、Project     获得父project,没有父时返回null
this.getParent()

//4、 Project     获得root 根project
this.getRootProject() // 永远不会返回null的
2、父project操作子project

1、传统思路:
父project中遍历获得子project,再操作子project。
2、使用Project类构造:
直接使用提供的构造 ,Project project(String path, Closure configureClosure)等构造操作。(本节使用方式)

(1)Project project(String path, Closure configureClosure)

project("app") {
    
     Project project ->
    // 这个安卓project指定输出类型 (安卓中application为输出apk,library为library库一般对应aar输出)
    apply plugins: "com.android.application" 
    group "sunnyDay" // 指定分组
    version "1.0.1"  // 版本
    dependencies {
    
    
        // 指定依赖闭包
    }
    android{
    
    
        //指定安卓闭包 安卓项目当然有安卓闭包
    }
}

参数1:project的路径(name),参数2:闭包,闭包代表指定路径的project
ps: 所以实际上完全可以在根工程中完成子工程中所有的配置,但是不建议这样做。模块化解耦好。

(2) void allprojects(Closure configureClosure)

allprojects {
    
    // 闭包参数默认为每个Project
 // 子类相同的配置可以在这里抽取配置
}

参数闭包,配置当前节点project和当前节点下所有子project

(3)void subprojects(Closure configureClosure)

subprojects {
    
      

    Project project->
    if (project.plugins.hasPlugin("com.android.library")){
    
     // 如果子工程为library工程 则做。。。。。
      //  apply from "xx/xx/build.gradle" // 引用某一project工程,这样就可以使用这个工程下的各种文件。
    }
}

1、当前节点下所有子节点的同一配置
2、闭包参数默认为每个子Project

(4)小结:上述三个方法的区别

范围不同:
1、第一个指定工程名获得相应工程对象。
2、第二个获得所有工程对象
3、第三个获得工程的子工程对象。

3、小收获
1、apply plugins: "xxx"// 指定输出类型(例如xxx为com.android.application) 
2、apply from "xx/xx/build.gradle" // 引用某一project工程,这样就可以使用这个工程下的各种文件。

四、详解:属性相关API

1、常见属性值
public interface Project extends Comparable<Project>, ExtensionAware, PluginAware {
    
    
    /**
     * The default project build file name.
     */
    String DEFAULT_BUILD_FILE = "build.gradle";

    /**
     * The hierarchy separator for project and task path names.
     */
    String PATH_SEPARATOR = ":";

    /**
     * The default build directory name.
     */
    String DEFAULT_BUILD_DIR_NAME = "build";

    String GRADLE_PROPERTIES = "gradle.properties";

    String SYSTEM_PROP_PREFIX = "systemProp";

    String DEFAULT_VERSION = "unspecified";

    String DEFAULT_STATUS = "release";
    ......
    }

可以看到Project类中默认了一些属性值的。常见属性值的意义如下:
1、String DEFAULT_BUILD_FILE = “build.gradle”:project 就是默认从这个文件读取配置,对自己初始化的。
2、String PATH_SEPARATOR = “:”:文件分隔符,文件中使用反斜杠,Gradle中使用冒号
3、String DEFAULT_BUILD_DIR_NAME = “build”:输出文件夹,存放工程的产出物。
4、String GRADLE_PROPERTIES = “gradle.properties”: 配置文件

2 、扩展属性

这里就有一个安卓工程为例子,因为安卓工程下,默认有个app子工程,我们不用在创建module了,直接拿来使用。
一般默认情况下app的build.gradle如下。是不是很熟悉,哈哈哈!

apply plugin: 'com.android.application'

android {
    
    
    compileSdkVersion = 29
    buildToolsVersion "29.0.1"
    defaultConfig {
    
    
        applicationId "com.sunnyday.gradleproject"
        minSdkVersion 21
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
    
    
        release {
    
    
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    
    
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.0.0-beta01'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.2'
    implementation 'com.google.android.material:material:1.0.0-beta01'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.1.0-alpha4'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0-alpha4'
}

问题:
可以看出,这个project内部的变量值都是写死的,比如compileSdkVersion = 29、applicationId "com.sunnyday.gradleproject"等等。我们每次修改都要修改其值,不太方便,那么能不能统一配置定义变量?这样我们需要更改时只需更改变量值即可。其实是可行的。解决如下。

(1)变量定义方式

定义变量,抽取,下次只需要修改变量值即可。
如下图,其实我们完全可以吧变量都抽取出来(此处为了方便抽了两个)

在这里插入图片描述
(2)使用扩展属性方式(其实也是变量的定义)

扩展属性语法:
ext{
    
    
  // 变量定义赋值
}

在这里插入图片描述

(3)扩展属性和直接定义变量的区别

其实写在一个build.gradle中 扩展属性和定义变量方式是没啥区别的,但是一个工程存在多个模块时。通用抽取时,扩展属性的优势就出来了。

3、扩展属性的优点应用

1、扩展属性优点:抽取到父节点
2、如上使用扩展属性简单的抽取了三条。接下来我们就以这三条为例子。进行抽取。

(1)菜鸡做法:直接抽取,多次定义。

在跟project的build.gradle为每个子工程定义扩展属性,子工程的build.gradle 就可直接使用(如下),编译通过。

在这里插入图片描述

在这里插入图片描述

(2)中级写法:定义一次多处使用

分析上面写法:
1、其实你为每个子工程指定了扩展属性,Project内部帮我们在每个子类中生成扩展属性。每个都生成还是耗性能的。
2、其实我们可以吧扩展属性直接定义在根工程,然后子工程获得根工程对象再直接复用。

// 根工程build.gradle中定义
    ext {
    
    
        mCompileSdkVersion =29
        mVersion = 1
        mApplicationId = 'com.sunnyday.gradleproject'
    }

// 子工程的build.gradle
android {
    
    
    // 子工程中获得根工程对象,直接使用
    compileSdkVersion = this.rootProject.mCompileSdkVersion
    buildToolsVersion "29.0.1"
    defaultConfig {
    
    
         // 子工程中获得根工程对象,直接使用
        applicationId this.rootProject.mApplicationId
        minSdkVersion 21
        targetSdkVersion 29
        // 子工程中获得根工程对象,直接使用
        versionCode this.rootProject.mVersion
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
 }

其实不用this.rootProject调用,直接compileSdkVersion =mCompileSdkVersion也是可以的,因为每个子project都会继承根project的所有属性,拿来直接使用。

(3)最终优化:自定义个build.gradle 文件

面对多模块,最终抽取演变而来的最终优化。

创建个build.gradle 文件
在这里插入图片描述

根工程引入文件
在这里插入图片描述
子工程使用
在这里插入图片描述

4、根工程的gradle.properties相关属性

在这里插入图片描述

其实以点properties结尾的文件都是配置文件,而且这种文件的内部都是以键值对的格式书写

(1)栗子:定义个sdk编译版本,子模块就可以直接通过键访问到这个值。

# 这里定义的键值对所有的工程都可以直接使用,这里都是键值对。键key,值object(使用注意使用方法转换toXXX())
mCompileSdkVersion=29

(2)是否使用某工程

# 是否引入某个工程,在这里配置true 或者false
isLoadXxxProject=false 

在这里插入图片描述

settiing.gradle 中配置下。如上安卓项目中,直接使用了app这个子project。
如果我们有个工程名字为XxxProject,gradle.properties 配置了false,则这里判断为不使用XxxProject,则XxxProject就为普通的文件夹(参看此工程上的小绿点消失)

五、详解:文件属性相关api

前面groovy的基础语法,我们也了解了gradle文件操作,这里主要是了解Project对象对文件的操作。在project下更加方便操作文件。

1、要点图

在这里插入图片描述

2、路径相关的api

主要是获得工程的一些重要目录。

(1)常用api

1、File getRootDir 获得根工程文件
2、File getBuildDir 获得当前工程的build文件
3、File getProjectDir 获得当前工程文件

(2)栗子

println getRootDir().name
println getBuildDir().name
println getProjectDir().name

优点:可以使用getRootDir获得当前工程绝对路径,然后在此绝对路径下,创建相对路径啦。

2、文件相关api

(1)文件定位

1、File file(String filename) 默认路径为当前工程路径
2、FileCollection files(Object… paths) 指定多个文件,返回文件对象集合
ps:以上是直接使用project对象的方法。这时传递的参数(file方法的)默认为当前project的路径,如果你使用new File的方式传递的是绝对路径。注意区别。

// 读取app目录下的build.gradle
getFileContext("./app/build.gradle")

/**
 * 读取文件内容
 * */
def getFileContext(String path) {
    
    
    try {
    
    
        def file = file(path)
        println file.text
    } catch (GradleException e) {
    
    
        println("file not found...")
    }
}

(2)文件拷贝

copy{
    
    
  from file // 要拷贝的文件
  into file // 拷贝到
  exclude{
    
    } // 排除不拷贝的文件或者文件夹
  rename{
    
    }// 重命名
}

例如:app目录下的a.txt 拷贝到根工程下
如下我们在app的build.gradle 中写代码(为了方便指定路径)
ps:file 可为文件对象,或者文件夹

copy{
    
    
    from file("a.txt")
    into getRootDir()
}

在这里插入图片描述

(3)文件树的遍历

fileTree(String file){
    
    
    FileTree tree ->
}

闭包参数 FileTree 对象
ps:Gradle会把文件映射成文件树

fileTree("./src/main/res"){
    
    
    FileTree tree ->tree.visit {
    
    // 使用FileTree 的visit 方法遍历文件
     FileTreeElement element->
         // 闭包参数文件树节点
         println(element.file.name)
    }
}

须知:
1、Gradle会把文件映射成文件树
2、fileTree(){}方法闭包参数为文件树对象
3、文件树对象的visit 方法可以遍历文件节点
4、文件节点的file属性可获得相应的文件对象

3、小结

以上方法不支持跨跟工程操作。

六、详解:其他api

1、知识图

在这里插入图片描述

2、依赖相关api

(1)主要方法


buildscript {
    
    
   // 1、配置工程的仓库地址
    repositories {
    
    }
    // 2、配置工程的“插件”依赖地址(注意这里的插件二字)
    dependencies {
    
    }
   }

(2)repositories 的栗子

buildscript {
    
     ScriptHandler scriptHandler ->
    // 1、配置工程的仓库地址
    scriptHandler.repositories {
    
    
            // 能调用哪些方法继续看方法源码的参数类即可
        RepositoryHandler repositoryHandle ->
            repositoryHandle.jcenter()
            repositoryHandle.google()
            repositoryHandle.mavenCentral()
            repositoryHandle.maven {
    
    
                //指定特定的maven地址(一般为公司内部的私有服务器地址)
                maven {
    
     url 'https://xxx/xxx' }
                name "name"// 还可以起个名字,一般为公司名,这里可以不写此句代码
                credentials {
    
    // 请求这个地址时密码要验证才可(为url请求添加密码限制)
                    username: "admin"
                    password: "admin123"
                }
            }
    }
    // 2、配置工程的“插件”依赖地址(注意这里的插件二字)
    scriptHandler.dependencies {
    
    
        // 能调用哪些方法继续看方法源码的参数类即可
    }

}

1、project的方法
2、查看源码发现参数为闭包,闭包变量为ScriptHandler类型(阅读注释)
3、ScriptHandler可以有哪些方法,继续看ScriptHandler类的源码即可
4、以下为 build script的完整写法(简单写法参看安卓根工程的build.script文件栗子)

(3)dependencies 栗子

1、dependencies :配置工程的插件依赖地址(注意这里的插件二字)
2、注意这个dependencies 在buildscript 闭包内,注意和工程下的dependencies 闭包区别

区别:
1、buildscript 闭包内的dependencies 依赖引入的是供gradle使用的
2、工程下的dependencies 依赖引入供我们的应用程序使用
ps:安卓栗子如下(安卓工程中)

// 根工程:
buildscript {
    
    
    repositories {
    
    
        google()
        jcenter()
    }
    dependencies {
    
    
        classpath 'com.android.tools.build:gradle:3.4.1'
        classpath 'com.jakewharton:butterknife-gradle-plugin:10.2.0'
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

// app 工程:
dependencies {
    
    
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.2.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
    }

开发过安卓的看着是不是很熟悉!!!

(4)工程下的dependencies 补充

implementation xxx,xxx可为的类型有哪些?
1、fileTree,file,files。根据要添加的本地文件/文件夹数目选择
2、第三方jar、aar
3、library库工程(使用implementation prooject(“libraryname”))
ps:常见问题:依赖冲突

(5)常见依赖冲突解决方式

dependencies {
    
    
    implementation 'com.jakewharton:butterknife:10.2.0'{
    
    
        // 方式1:排除某一模块
        exclude{
    
    
            module:"support-v4"
        }
        // 方式2:排除某一组下的全部库
        exclude{
    
    
            group:"com.android.support"
        }
         transitive false // 传递依赖
    }
}

(5)传递依赖

本工程是否可以使用远程依赖包里依赖的依赖

dependencies {
    
    
    implementation 'com.jakewharton:butterknife:10.2.0'{
    
    
         //本工程是否可以使用远程依赖包里依赖的依赖
         transitive false // 传递依赖
    }
}

在这里插入图片描述

一般不建议使用,默认为false不使用。
因为在工程B的升级过程中可能会废弃对工程C的依赖,这样A就出现Bug了。

3、外部命令执行

跨工程操作文件,使用linux等cmd命令

// 跨工程操作文件
task(name: 'apkcopy') {
    
    // 指定任务名
    doLast {
    
    // gradle的指定阶段去执行
        def sourcePath = this.buildDir.path + "outPut/apk"
        def destinationPath = "硬盘位置(自己随便定义)"
        def command = "mv f ${sourcePath} ${destinationPath}"
        exec {
    
    // 执行外部命令(linux命令)
            try {
    
    

            } catch (GradleException e) {
    
    
                println("exec failed")
            }
            executable "bash" // 代表执行外部命令
            args "-c", command
            println("exec success")
        }
    }
}

1、task简单了解,参数指明任务名字
2、doLast闭包指明task在执行阶段执行
3、exec用于执行外部命令
4、一般exec内的语法固定不变,只需改变外部命令即可

The end

下节:Task相关知识点敬请期待!!!

猜你喜欢

转载自blog.csdn.net/qq_38350635/article/details/102808161
今日推荐