Android Gradle学习系列(六)-Gradle核心之Project详解及实战

前言

这一篇我们就开始讲解Gradle编程中很重要的一个概念Project,它就像我们Android中的Activity一样,是我们Gradle编译程序的时候每天都会见到的,下面我们就开始说说这个Project

1.深入了解Project

我们首先使用AS打开我们的实战项目HelloGradle
在这里插入图片描述
这里我新建了几个模块,模拟我们在真实项目中的项目结构.我们一般称这个HelloGradleProject,而app,base,http,video我们成为module,也就是说一个Project下面可以有多个Module,而每个Module下面又可以有多个Module,但是我们一般不会这么做,所以我们在IDEA中的项目结构一般为:

+Project
--+Module
----+Module
----+Module
--+Module
--+Module

但是对于我们Gradle来说不是这样的,首先HelloGradle是一个Project,这肯定是毋庸置疑的,但是对于我们Gradle来说,像app,base等也是一个Project,我们下面就来验证下到底是不是这样的

huangfushengdeMacBook-Pro:HelloGradle huangfusheng$ ./gradlew projects

> Configure project :app
WARNING: The following project options are deprecated and have been removed: 
android.enableAapt2
This property has no effect, AAPT2 is now always used.



> Configure project :base
WARNING: The following project options are deprecated and have been removed: 
android.enableAapt2
This property has no effect, AAPT2 is now always used.



> Configure project :http
WARNING: The following project options are deprecated and have been removed: 
android.enableAapt2
This property has no effect, AAPT2 is now always used.



> Configure project :video
WARNING: The following project options are deprecated and have been removed: 
android.enableAapt2
This property has no effect, AAPT2 is now always used.



> Task :projects

------------------------------------------------------------
Root project
------------------------------------------------------------

Root project 'HelloGradle'
+--- Project ':app'
+--- Project ':base'
+--- Project ':http'
\--- Project ':video'

To see a list of the tasks of a project, run gradlew <project-path>:tasks
For example, try running gradlew :app:tasks

BUILD SUCCESSFUL in 0s
1 actionable task: 1 executed

我们执行了./gradlew projects指令,最终我们看到了结果,有一个Root Project和四个Sub Project,这就验证了我们刚才说的对于Gradle来说每个Module都是一个Project
同时我们每个Project都有一个共同点,那就是都必须有一个build.gradle,不管你是根Project还是子Project.反之如果你没有这个build.gradle,那么就不是一个Project,像我们的build,就只能称之为文件夹,而且我们的Project最终由谁去配置管理呢?就是我们这个build.gradle文件.根Projectbuild.gradle的作用基本上就是管理子Project,子Projectbuild.gradle作用就是输出,像我们这个app对应就是输出apk,像base就种就是输出aar

2.Project核心API讲解

在这里插入图片描述
Project中的API非常多,我们把它分成6组来学习,每组学习主要的API,这样的话效率上会很高

2.1Project相关API

2.1.1 getAllprojects()

这个方法获取工程中所有的project(包括根project与子project
我们之前验证gradle整体结构的时候使用过一个指令./gradlew projects,输出我们当前项目的所有Project,它其实对应的就是我们getAllprojects()这个方法,我们下面就写一段代码来打印下我们项目中的Project都有哪些并且输出对应的名字
在这里插入图片描述
接下来我们调用下这个方法,怎么执行呢?很简单,我们随便执行一个Gradle的任务都会执行我们这个方法
在这里插入图片描述
注意,我们现在在build.gradle中写的代码都是在我们Gradle配置阶段执行的,那如何让我们的代码在Gradle执行阶段执行呢?我们后面学习Task的时候为大家讲解

2.1.2 getSubProjects()

这个方法时获取当前project下,所有的子project,注意:这个返回结果可能为null
在这里插入图片描述
我们先在根Project下面使用下这个方法,看看打印的结果
在这里插入图片描述
接着我们在appbuild.gradle下面写上这段代码,把根projectbuild.gradle里面的这段代码注释掉,我们运行下,看看打印的结果
在这里插入图片描述
在这里插入图片描述
因为app下面没有子Project,所以什么都没有打印,也就是返回null

2.1.3 getParent()

获取当前project的父project(若在rooProjectbuild.gradle调用,则返回null
我们现在appbuild.gradle里面调用下

在这里插入图片描述
接着我们在根工程下的build.gradle里面调用下
在这里插入图片描述
这里我们发现根本就编译不过,编译器告诉我们在根工程下面调用getParent()会返回null

2.1.4 getRootProject()

取项目的根project(一定不会为null
我们同样分两部分来调用下,一部分在app工程下,一部分在根工程下
我们先在app工程下调用下
在这里插入图片描述
接着我们在根工程下调用
在这里插入图片描述
项目的根工程只有一个,所以不管在哪打印出来的值都是一样的并且不为空

2.1.5 project(String path, Closure configureClosure)

这个方法是根据path找到对应的project,通过闭包进行配置(闭包的参数是path对应的project对象)
在这里插入图片描述
这个方法会通过路径path找对应的project,如果没找到就会抛出个异常,我们试验下
在这里插入图片描述
我们这里传入一个不存在的工程test,结果编译不过了,下面我们就来配置下我们存在的工程app (这里有个地方说下,就是如果参数的最后一个是闭包,我们是可以拿到括号外边的,这个是我们在讲groovy语法介绍过得)

我们先通过这个方法输出下我们path对应工程的名字

在这里插入图片描述
我们在平时开发不可能只输出个名字这么简单,那都能配置什么呢?由于我们的参数是project,所以project里面的方法都可以调用
我们先看下appbuild.gradle里面的内容
在这里插入图片描述
这里我们看见有三样东西,那么我们在根工程下至少能为我们的app这个工程配置这三样东西

在这里插入图片描述
这里虽然可以这样写,但是实际我们基本上不会这样处理的,我们每个projectbuild.gradle最好都是只配置自己的东西,独立起来

2.1.6 allprojects(Closure configureClosure)

配置当前project和其子project的所有project
在这里插入图片描述
这里我们就假定所有的工程groupcom.hfs,version1.0.0-release
在这里插入图片描述

我们就用我们的http工程来试验下,打印它对应的groupversion,看看是不是和我们写的一样

我们先看下http工程的build.gradle内容
在这里插入图片描述
接着我们执行下我们修改的代码:
在这里插入图片描述

2.1.7 subprojects(Closure configureClosure)

配置子project的所有project(不包含当前project
在这里插入图片描述
我们实际开通过程中有的时候会把我们写好的组件上传到对应的maven服务器上,这个时候每个子工程可能都需要写一个上传到maven服务器的插件,这个时候我们就可以通过这个方法统一为我们的子project统一添加

在这里插入图片描述

2.2Task相关API

这部分我们不在这一篇进行讲解,我们会在下一篇单独写一下Task,跟我们这一篇写Project一样,这部分会在哪里进行详细讲解

2.3 属性相关API

我们首先点开Project的源码中看看它都有哪些主要的属性
在这里插入图片描述
首先就是默认的编译文件build.gradle,这也就是为什么我们每个工程下面都要有一个build.gradle文件;第二个就是我们Project的路径分隔符,这里不区分系统,都是:;第三个就是我们每个工程默认的输出文件夹,每个工程编译完后都会出现;下一个属性gradle.properties我们会在下面进行讲解,剩下的就都不太主要了,我们在开发中一般也不会用到

2.3.1 在gradle脚本文件中使用ext扩展属性

我们先随便看下我们某个工程的build.gradle文件,这里以video工程为例

apply plugin: 'com.android.library'

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.0"


    defaultConfig {
        minSdkVersion 19
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        consumerProguardFiles 'consumer-rules.pro'
    }

    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.1.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.2.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}

我们在写我们应用程序的时候,我们知道在类中尽量不要使用固定的数字或者字符串的,我们称之为魔法值,我们一般是定义成常量来使用,那我们回头看我们这个build.gradle文件,如果我们也把它看成一个类的话,里面是不是有好多魔法值啊,我们就需要对他们进行统一的定义.这里就需要我们的扩展属性了,那怎么写呢?很简单,使用ext{}就可以,把你想要定义的扩展属性都写在闭包中即可,这里我们就举两个例子
在这里插入图片描述
我们项目中一般会有好多个工程,每个工程都需要配置这两个属性,我们难道要在每个工程的build.gradle中都写这一段代码吗?我们回想下我们上面讲的subprojects这个方法,我们是不是可以在这个方法里面为我们每个子工程配置啊,由于父project中的属性子project可以直接访问,所以我们的子工程不需要懂任何代码即可,我们实践下看看效果

我们现在根工程下配置下
在这里插入图片描述
然后在我们的子工程中使用下,这里我们换成http工程来验证下
在这里插入图片描述

编译完没发现问题,说明这么做是可以的,但是这种方式还能不能改进呢?我们想下,我们通过subprojects来为我们每个子工程都定义了扩展属性,虽然我们这里只写了一次,但是gradle在编译的时候都会为子工程定义一遍扩展属性,所以从本质上来说我们每个子工程还是定义了一个扩展属性块,只不过现在是由gradle帮我们自动进行,那应该怎么改进呢?别急,我们之前讲过子工程可以通过getRootProject这个方法来获取根工程,那么我们是不是可以直接把扩展属性定义在根工程中呢?然后在每个子工程复用这个根工程中的扩展属性,做法很简单,直接把这个扩展属性块从我们的subProjects中拿出来

在这里插入图片描述

我们在子工程中通过rootProject来获取相应属性
在这里插入图片描述

这里其实我们不加这个rootProject也可以,这里就相当于继承关系,根工程里面的属性会被子工程继承,所以子工程可以直接使用根工程的属性

到这里我们离最优的方式就差一步了,这里肯定会有同学说你为啥不一开始就说最优的呢?这个主要就演示下这个演进过程,那我们还能对它进行怎么优化呢?这里就是我们把我们的扩展属性都定义在一个gradle文件中,这样就可以减少我们根工程下的配置代码,下面我们就来看下
这里我就拿我之前写好的来展示下,具体格式都是差不多了

/**
 *  全局统一配置文件
 */
ext {
    //是否单独开发每个模块
    isModule = false
    //版本号
    versions = [
            applicationId                : "com.hfs.latteframework",        //应用ID
            versionCode                  : 1,                    //版本号
            versionName                  : "1.0.0",              //版本名称

            compileSdkVersion            : 28,
            buildToolsVersion            : "28.0.3",
            minSdkVersion                : 19,
            targetSdkVersion             : 28,

            androidSupportSdkVersion     : "28.0.0",
            constraintLayoutVersion      : "1.1.1",
            runnerVersion                : "1.0.1",
            espressoVersion              : "3.0.1",
            junitVersion                 : "4.12",
            annotationsVersion           : "24.0.0",
            javaSDKVersion               : 1.8,

            multidexVersion              : "1.0.2",
            butterknifeVersion           : "8.4.0",
            arouterApiVersion            : "1.4.0",
            arouterCompilerVersion       : "1.2.1",
            arouterannotationVersion     : "1.0.4",
            eventbusVersion              : "3.0.0",
            novateVersion                : "1.5.5",
            loggerVersion                : "2.2.0",
            fastjsonVersion              : "1.1.54",
            immersionbarVersion          : "2.3.2-beta05",
            glideVersion                 : "4.8.0",
            bannerVersion                : "2.1.4",
            javaxVersion                 : "1.2",
            lombokVersion                : "1.16.6",
            greendaoVersion              : "3.2.2",
            pickerViewVersion            : "4.1.6",
            superAdapterVersion          : "3.6.8",
            scaleImageViewVersion        : "3.10.0",
            zxingViewVersion             : "1.3",
            xxpermissionsVersion         : "5.5",
            rx2JavaVersion               : "2.1.5",
            rx2AndroidVersion            : "2.0.1",
            photoview                    : "1.2.4",
            smartRefreshLayout           : "1.0.5.1",
            baseRecyclerViewAdapterHelper: "2.9.42",
            bugly                        : "latest.release",
            kprogresshud                 : "1.1.0",
            stetho                       : "1.5.0",
            alertview                    : "1.0.3",
            VerticalTabLayout            : "1.2.5",
            FlycoTabLayout               : "2.2.1.2@aar",
            leakcanary                   : "1.5.1"


    ]
    //引用的依赖
    dependencies = [
            "appcompat_v7"                 : "com.android.support:appcompat-v7:${versions["androidSupportSdkVersion"]}",
            "constraint_layout"            : "com.android.support.constraint:constraint-layout:${versions["constraintLayoutVersion"]}",
            "runner"                       : "com.android.support.test:runner:${versions["runnerVersion"]}",
            "espresso_core"                : "com.android.support.test.espresso:espresso-core:${versions["espressoVersion"]}",
            "junit"                        : "junit:junit:${versions["junitVersion"]}",
            "support_annotations"          : "com.android.support:support-annotations:${versions["annotationsVersion"]}",
            "design"                       : "com.android.support:design:${versions["androidSupportSdkVersion"]}",
            "support-v4"                   : "com.android.support:support-v4:${versions["androidSupportSdkVersion"]}",
            "cardview-v7"                  : "com.android.support:cardview-v7:${versions["androidSupportSdkVersion"]}",
            "recyclerview-v7"              : "com.android.support:recyclerview-v7:${versions["androidSupportSdkVersion"]}",

            //方法数超过65535解决方法64K MultiDex分包方法
            "multidex"                     : "com.android.support:multidex:${versions["multidexVersion"]}",

            //路由
            "arouter_api"                  : "com.alibaba:arouter-api:${versions["arouterApiVersion"]}",
            "arouter_compiler"             : "com.alibaba:arouter-compiler:${versions["arouterCompilerVersion"]}",
            "arouter_annotation"           : "com.alibaba:arouter-annotation:${versions["arouterannotationVersion"]}",

            //黄油刀
            "butterknife_compiler"         : "com.jakewharton:butterknife-compiler:${versions["butterknifeVersion"]}",
            "butterknife"                  : "com.jakewharton:butterknife:${versions["butterknifeVersion"]}",

            //事件订阅
            "eventbus"                     : "org.greenrobot:eventbus:${versions["eventbusVersion"]}",

            //网络
            "novate"                       : "com.tamic.novate:novate:${versions["novateVersion"]}",

            //日志
            "logger"                       : "com.orhanobut:logger:${versions["loggerVersion"]}",

            //fastJson
            "fastjson"                     : "com.alibaba:fastjson:${versions["fastjsonVersion"]}.android",

            //沉浸式状态栏
            "immersionbar"                 : "com.gyf.immersionbar:immersionbar:${versions["immersionbarVersion"]}",

            //banner
            "banner"                       : "com.bigkoo:ConvenientBanner:${versions["bannerVersion"]}",

            //图片加载
            "glide"                        : "com.github.bumptech.glide:glide:${versions["glideVersion"]}",

            //lombok
            "lombokJavax"                  : "javax.annotation:javax.annotation-api:${versions["javaxVersion"]}",
            "lombok"                       : "org.projectlombok:lombok:${versions["lombokVersion"]}",

            //数据库
            "greenDao"                     : "org.greenrobot:greendao:${versions["greendaoVersion"]}",

            //时间,地址,条件选择器
            "pickerView"                   : "com.contrarywind:Android-PickerView:${versions["pickerViewVersion"]}",

            //展示大图+手势滑动
            "scaleImageView"               : "com.davemorrissey.labs:subsampling-scale-image-view:${versions["scaleImageViewVersion"]}",

            //二维码扫描
            "zxing"                        : "com.github.0xZhangKe:QRCodeView:${versions["zxingViewVersion"]}",

            //危险权限库
            "xxpermissions"                : "com.hjq:xxpermissions:${versions["xxpermissionsVersion"]}",

            //RX家族
            "rx2_java"                     : "io.reactivex.rxjava2:rxjava:${versions["rx2JavaVersion"]}",
            "rx2_android"                  : "io.reactivex.rxjava2:rxandroid:${versions["rx2AndroidVersion"]}",

            //图片缩放
            "photoview"                    : "com.github.chrisbanes.photoview:library:${versions["photoview"]}",

            //SmartRefreshLayout
            "smartRefreshLayout"           : "com.scwang.smartrefresh:SmartRefreshLayout:${versions["smartRefreshLayout"]}",

            //baseRecyclerViewAdapterHelper
            "baseRecyclerViewAdapterHelper": "com.github.CymChad:BaseRecyclerViewAdapterHelper:${versions["baseRecyclerViewAdapterHelper"]}",

            //Bugly集成
            "bugly"                        : "com.tencent.bugly:crashreport_upgrade:${versions["bugly"]}",

            //仿ios进度条 已抽取到lib中
            "kprogresshud"                 : "com.kaopiz:kprogresshud:${versions["kprogresshud"]}",

            //安卓调试神器-Stetho
            "stetho"                       : "com.facebook.stetho:stetho:${versions["stetho"]}",
            "stetho-okhttp3"               : "com.facebook.stetho:stetho-okhttp3:${versions["stetho"]}",

            // 仿ios弹出对话窗体 已抽取到lib中
            "alertview"                    : "com.bigkoo:alertview:${versions["alertview"]}",

            //垂直的tabLayout
            "VerticalTabLayout"            : "q.rorbin:VerticalTabLayout:${versions["VerticalTabLayout"]}",

            //水平tablayout
            "FlycoTabLayout"               : "com.flyco.tablayout:FlycoTabLayout_Lib:${versions["FlycoTabLayout"]}",

            //leakcanary内存泄露
            "leakcanary-android"           : "com.squareup.leakcanary:leakcanary-android:${versions["leakcanary"]}",
            "leakcanary-android-no-op"     : "com.squareup.leakcanary:leakcanary-android-no-op:${versions["leakcanary"]}",


    ]

}

我们看这份定义文件,主要就是定义了两个map,一个是versions,一个是dependencies,当然你也可以按照你的定义方式多定义几个map,那我们怎么使用呢?
首先要在你的根工程的build.gradle中引入这个配置文件,一般写在最顶部

apply from: "config.gradle"

之后子工程就可以直接使用了,我这里还是拿我写的一个项目来说

apply plugin: 'com.android.library'
apply plugin: 'com.jakewharton.butterknife'
apply plugin: 'org.greenrobot.greendao'

android {
    compileSdkVersion rootProject.ext.versions.compileSdkVersion
    buildToolsVersion rootProject.ext.versions.buildToolsVersion

    defaultConfig {
        minSdkVersion rootProject.ext.versions.minSdkVersion
        targetSdkVersion rootProject.ext.versions.targetSdkVersion
        versionCode rootProject.ext.versions.versionCode
        versionName rootProject.ext.versions.versionName
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        //MultiDex分包方法
        multiDexEnabled true

        //Arouter路由配置
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [AROUTER_MODULE_NAME: project.getName()]
                includeCompileClasspath = true
            }
        }

    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    //防止编译的时候oom、GC
    dexOptions {
        javaMaxHeapSize "4g"
    }
    compileOptions {
        targetCompatibility rootProject.ext.versions.javaSDKVersion
        sourceCompatibility rootProject.ext.versions.javaSDKVersion
    }
}

dependencies {
    // 在项目中的libs中的所有的.jar结尾的文件,都是依赖
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    //把implementation 用api代替,它是对外部公开的, 所有其他的module就不需要添加该依赖
    api rootProject.ext.dependencies["appcompat_v7"]
    api rootProject.ext.dependencies["constraint_layout"]
    api rootProject.ext.dependencies["cardview-v7"]
    api rootProject.ext.dependencies["recyclerview-v7"]
    api rootProject.ext.dependencies["support-v4"]
    api rootProject.ext.dependencies["design"]
    api rootProject.ext.dependencies["support_annotations"]
    api rootProject.ext.dependencies["junit"]
    //MultiDex分包方法
    api rootProject.ext.dependencies["multidex"]
    //黄油刀
    annotationProcessor rootProject.ext.dependencies["butterknife_compiler"]
    api rootProject.ext.dependencies["butterknife"]
    //Arouter路由
    annotationProcessor rootProject.ext.dependencies["arouter_compiler"]
    api rootProject.ext.dependencies["arouter_api"]
    api rootProject.ext.dependencies["arouter_annotation"]
    //eventbus 发布/订阅事件总线
    api rootProject.ext.dependencies["eventbus"]
    //日志
    api rootProject.ext.dependencies["logger"]
    //fastJson
    api rootProject.ext.dependencies["fastjson"]
    //沉浸栏
    api rootProject.ext.dependencies["immersionbar"]
    //banner
    api rootProject.ext.dependencies["banner"]
    //图片加载
    api rootProject.ext.dependencies["glide"]
    //lombok
    api rootProject.ext.dependencies["lombok"]
    api rootProject.ext.dependencies["lombokJavax"]
    //时间 日期  地址 条件选中器
    api rootProject.ext.dependencies["pickerView"]
    //GreenDao
    api rootProject.ext.dependencies["greenDao"]
    //scaleImageView
    api rootProject.ext.dependencies["scaleImageView"]
    //二维码扫描
    api rootProject.ext.dependencies["zxing"]
    //危险权限
    api rootProject.ext.dependencies["xxpermissions"]
    //RX
    api rootProject.ext.dependencies["rx2_java"]
    api rootProject.ext.dependencies["rx2_android"]
    //smartRefreshLayout
    api rootProject.ext.dependencies["smartRefreshLayout"]
    //baseRecyclerViewAdapterHelper
    api rootProject.ext.dependencies["baseRecyclerViewAdapterHelper"]
}

我们这种写法呢看上去比较清晰,每个子工程也不需要进行单独的配置了,修改什么东西直接在我们这个配置文件中修改即可

2.3.2 在gradle.properties文件中使用扩展属性

这小节我们就简单说下扩展属性的另外一种方式,其实也很简单,只需要在我们gradle.properties中定义即可,从名字也可以看出这个文件就是gradle的属性
这里定义的只能是key-value方式,不能像我们上面那种方式那样,我们举个例子
我们在gradle.properties中定义一个属性isLoadHttp来标识我们是否加载http这个工程
在这里插入图片描述
然后我们在setting.gradle中使用下
在这里插入图片描述
我们先把这个属性改成true
在这里插入图片描述

我们看见我们这个http就变成我们项目的一个工程,接下来我们把这个属性改成false
在这里插入图片描述
我们看见这个http就变成了一个普通的文件夹了
我们在这文件中也可以定义我们上小节中的变量,我们就拿其中一个举例
在这里插入图片描述
我们在我们的video工程中使用下
在这里插入图片描述
我们总结下:在gradle.properties中定义的属性,可以直接访问,但得到的类型为Object,一般需要通过toXXX()方法转型。

2.4 File相关API

在这里插入图片描述

2.4.1 getRootDir()

获取rootProject目录
在这里插入图片描述

2.4.2 getBuildDir()

获取当前projectbuild目录,这里我们都知道我们每个project下面都会有一个build文件夹

在这里插入图片描述

2.4.3 getProjectDir()

获取当前project目录

我们分别在根projectvideo project下的build.gradle中打印下
在这里插入图片描述

2.4.4 File file(Object path)

以我们当前的project来寻找文件,这里我们在我们的根工程下新建一个test.gradle,里面随便写点东西方便我们一会打印
在这里插入图片描述

接着我们就使用file这个方法来找下这个文件,找到了我们就输出里面的内容,找不到就输出文件找不到
在这里插入图片描述
我们执行下这段代码
在这里插入图片描述

我们在试一下传入一个不存在的文件路径
在这里插入图片描述
直接编译失败

2.4.5 files(Object... paths)

这个方法和file类似,它的入参是一个可变参数,返回值是ConfigurableFileCollection,就是一次可以定位多个文件,我们可以遍历这个返回值进行一系列操作

2.4.6 copy(Closure closure)

文件的拷贝,这里既可以拷贝文件也可以拷贝文件夹
我们先拷贝个文件,我们现在app工程下新建一个app.txt,我们将他拷贝到我们的根工程build文件夹下
在这里插入图片描述
我们运行下,看看根工程build文件夹下面有没有这个文件
在这里插入图片描述

接下来我们拷贝一个文件夹试试,我们就把app--outputs--logs这个文件夹拷贝到根工程的build文件夹下

在这里插入图片描述
我们运行下

在这里插入图片描述

我们还可以在闭包中指定我们不想被拷贝的文件,用exclude{}
举例:

 exclude { details ->
        details.file.name.endsWith('.html')
    }

我们还可以对拷贝的文件进行重命名rename{}
举例:

rename { String fileName ->
        fileName.replace('test', 'test1')
    }

2.4.7 fileTree(Object baseDir, Closure configureClosure)

定位一个文件树(目录+文件),可对文件树进行遍历
我们现在app工程根目录下新建一个文件夹file,同时我们在这个文件夹下新建几个文件
在这里插入图片描述
我们就来输出下这几个文件的名字,同时将他们拷贝到根工程的build文件夹下
在这里插入图片描述

我们运行下
在这里插入图片描述

在这里插入图片描述

2.5 Gradle生命周期API

这部分我们在上一篇已经将讲解过了,这里也就不再讲解了
Android Gradle学习系列(五)-Gradle生命周期探索

2.6 其它API

2.6.1 依赖相关API

在这里插入图片描述
谈到我们的依赖相关api我们就不得不提一个方法buildscript{},我们每次新建一个项目,在项目的根工程的build.gradle中都会有这个方法,这个方法里面就是我们依赖配置的核心部分,那这里面都能配置哪些内容呢?我们看下这个方法的源码就知道了

在这里插入图片描述
源码中注释告诉我们闭包的参数的类型是ScriptHandler,由此我们猜想我们能在闭包中调用哪些方法由ScriptHandler这个类决定,我们继续看下ScriptHandler这个类的源码

public interface ScriptHandler {
    /**
     * The name of the configuration used to assemble the script classpath.
     */
    String CLASSPATH_CONFIGURATION = "classpath";

    /**
     * Returns the file containing the source for the script, if any.
     *
     * @return The source file. Returns null if the script source is not a file.
     */
    @Nullable
    File getSourceFile();

    /**
     * Returns the URI for the script source, if any.
     *
     * @return The source URI. Returns null if the script source has no URI.
     */
    @Nullable
    URI getSourceURI();

    /**
     * Returns a handler to create repositories which are used for retrieving dependencies for the script classpath.
     *
     * @return the repository handler. Never returns null.
     */
    RepositoryHandler getRepositories();

    /**
     * Configures the repositories for the script dependencies. Executes the given closure against the {@link
     * RepositoryHandler} for this handler. The {@link RepositoryHandler} is passed to the closure as the closure's
     * delegate.
     *
     * @param configureClosure the closure to use to configure the repositories.
     */
    void repositories(Closure configureClosure);

    /**
     * Returns the dependencies of the script. The returned dependency handler instance can be used for adding new
     * dependencies. For accessing already declared dependencies, the configurations can be used.
     *
     * @return the dependency handler. Never returns null.
     * @see #getConfigurations()
     */
    DependencyHandler getDependencies();

    /**
     * Configures the dependencies for the script. Executes the given closure against the {@link DependencyHandler} for
     * this handler. The {@link DependencyHandler} is passed to the closure as the closure's delegate.
     *
     * @param configureClosure the closure to use to configure the dependencies.
     */
    void dependencies(Closure configureClosure);

    /**
     * Returns the configurations of this handler. This usually contains a single configuration, called {@value
     * #CLASSPATH_CONFIGURATION}.
     *
     * @return The configuration of this handler.
     */
    ConfigurationContainer getConfigurations();

    /**
     * Returns the {@code ClassLoader} which contains the classpath for this script.
     *
     * @return The ClassLoader. Never returns null.
     */
    ClassLoader getClassLoader();
}

这个源码里面的get方法我们不关心,剩下的有void repositories(Closure configureClosure);void dependencies(Closure configureClosure);这两个方法,所以我们buildscript{}里面可以这么写了
在这里插入图片描述

我们首选看第一个方法repositories{},这个是配置我们工程的仓库地址,那都能配置哪些仓库呢?我们看下它的源码
在这里插入图片描述

同样的我们通过注释得知,决定的是我们这个RepositoryHandler类,我们看下RepositoryHandler源码

public interface RepositoryHandler extends ArtifactRepositoryContainer {

    FlatDirectoryArtifactRepository flatDir(Closure configureClosure);

    MavenArtifactRepository jcenter();

    MavenArtifactRepository mavenCentral();

    MavenArtifactRepository mavenLocal();

    MavenArtifactRepository google();

    MavenArtifactRepository maven(Closure closure);

    IvyArtifactRepository ivy(Closure closure);

}

通过源码我们知道常用的就是上面那几种,我们分别写下
在这里插入图片描述
OK,接下来我们看下另外一个方法dependencies{},这里我们突出这个方法是配置我们的插件依赖地址,为啥呢?因为我们的Project中也有这个方法
在这里插入图片描述
也就是我们工程的build.gradle中也是可以调用一个叫dependencies名字的方法,但是和我们在buildscript方法中调用是完全不同的.我们想一下我们的gradle也是一个编程框架,那么我们在编写gradle的时候就会用到一些第三方的东西,在buildscriptdependencies就是来指定我们要依赖哪第三方的库,而我们projectbuild.gradledependencies是我们这个工程要依赖的第三方库
在这里插入图片描述
其实我们上面写的代码可以简化一下,
在这里插入图片描述

这样看起来是不是就很熟悉了,跟我们在项目中写法就完全一样了吧
我们下面说一说build.gradle中的dependencies方法,我们以我们app工程为例子

dependencies {
	//第一种:依赖文件树
    compile fileTree(dir: 'libs', include: ['*.jar']) 
    // compile file() // 依赖单个文件
    // compile files() // 依赖多个文件
    // 第二种:依赖仓库中的第三方库
    compile 'com.android.support:appcompat-v7:26.1.0' 
    // 第三种依赖工程下其他Module
    compile project('video') {
      exclude module: 'support-v4' // 排除依赖:排除指定module
      exclude group: 'com.android.support' // 排除依赖:排除指定group下所有的module
      transitive false // 禁止传递依赖,默认值为false
    }
  
    // 栈内编译
    provided('com.tencent.tinker:tinker-android-anno:1.9.1')
}

传递依赖:
在这里插入图片描述
我们一般是禁止传递依赖的,也就是工程A是禁止直接调用工程C中的依赖库的方法,只有工程B可以,因为有可能哪天工程B升级不再依赖工程C了,结果工程A还在使用,就会在编译过程中出现问题

下面我们来说说这个provided

  • 依赖包只在编译期起作用。(如:tinkertinker-android-anno只用于在编译期生成Application类,同时我们并不需要把该库中类打包进apk,这样可以减小apk包体积)
  • 被依赖的工程中已经有了相同版本的第三方库,为了避免重复引用,可以使用provided

2.6.2 外部命令执行

在这里插入图片描述

发布了87 篇原创文章 · 获赞 319 · 访问量 149万+

猜你喜欢

转载自blog.csdn.net/Greathfs/article/details/102809381