Gradle随记---初识

谈起Gradle开发,自然就需要先从最基础的两个配置文件build.gradle以及settings.gradle说起,那其实对应的Gradle API类就是org.gradle.api.Projectorg.gradle.api.initialization.Settings,而定义在文件中熟悉的闭包配置对应的则是类方法以及各种拓展Extension。

Settings

对于Settings的认识,相信大多数都是停留在settings.gradle文件中添加include项目配置,但是能做的不仅于此,我们就此从创建、设置加载来完整了解一遍。

创建

Settings的创建跟org.gradle.initialization.ScriptEvaluatingSettingsProcessor以及org.gradle.initialization.SettingsFactory两个类息息相关。
Gradle首先会通过BuildScopeServicescreateSettingsProcessor方法创建一个SettingsProcessor,这个类主要是通过ScriptEvaluatingSettingsProcessor来进行构造,然后借助其process方法来创建Settings的实例。

public SettingsInternal process(GradleInternal gradle,
                                    SettingsLocation settingsLocation,
                                    ClassLoaderScope buildRootClassLoaderScope,
                                    StartParameter startParameter) {
        Timer settingsProcessingClock = Time.startTimer();
        Map<String, String> properties = propertiesLoader.mergeProperties(Collections.<String, String>emptyMap());
        TextResourceScriptSource settingsScript = new TextResourceScriptSource(textResourceLoader.loadFile("settings file", settingsLocation.getSettingsFile()));
        SettingsInternal settings = settingsFactory.createSettings(gradle, settingsLocation.getSettingsDir(), settingsScript, properties, startParameter, buildRootClassLoaderScope);
        applySettingsScript(settingsScript, settings);
        LOGGER.debug("Timing: Processing settings took: {}", settingsProcessingClock.getElapsed());
        return settings;
    }

从上述代码中得知该方法主要做了三件事情:

  1. 根据settings.gradle文件构造TextResourceScriptSource对象settingsScript
  2. 通过settingsFactory创建一个SettingsInternal实例
  3. 调用applySettingsScript方法将settings.gradle中的闭包配置装载到SettingsInternal实例中

第2和3步都需要用到TextResourceScriptSource,这个具体作用是什么呢?

TextResourceScriptSource

public class TextResourceScriptSource implements ScriptSource {
    private final TextResource resource;
    private String className;
    ...
}

这个类主要作用是通过URI读取指定的配置脚本源码以及生成对应的className,这里对应的就是settings.gradle配置文件,并且作为构造函数参数生成ScriptCompiler来把脚本文件编译成一个object对象。

SettingsFactory

再来看下SettingsFactorycreateSettings方法:

public class SettingsFactory {
    ...
    public SettingsInternal createSettings(GradleInternal gradle, File settingsDir, ScriptSource settingsScript,
                                           Map<String, String> gradleProperties, StartParameter startParameter,
                                           ClassLoaderScope buildRootClassLoaderScope) {

        ClassLoaderScope settingsClassLoaderScope = buildRootClassLoaderScope.createChild("settings");
        ScriptHandlerInternal settingsScriptHandler = scriptHandlerFactory.create(settingsScript, settingsClassLoaderScope);
        DefaultSettings settings = instantiator.newInstance(DefaultSettings.class,
            serviceRegistryFactory, gradle,
            settingsClassLoaderScope, buildRootClassLoaderScope, settingsScriptHandler,
            settingsDir, settingsScript, startParameter
        );
        ...
        return settings;
    }
}

通过Instantiator创建了一个DefaultSettings类型的实例。
到这里settings.gradle对应的Settings实例就创建完毕了,接着就是把定义在文件中的闭包设置加载到实例中,整个过程就结束了。

配置

还记得上面的SettingsInternalprocess第三步吗,那就是对settings.gradle文件中定义的配置进行应用。

private void applySettingsScript(TextResourceScriptSource settingsScript, final SettingsInternal settings) {
        ClassLoaderScope settingsClassLoaderScope = settings.getClassLoaderScope();
        ScriptPlugin configurer = configurerFactory.create(settingsScript, settings.getBuildscript(), settingsClassLoaderScope, settings.getRootClassLoaderScope(), true);
        configurer.apply(settings);
}

通过ScriptPluginFactory生成ScriptPlugin,然后调用其apply方法加载对应的配置。

Settings.gradle

对于settings.gradle文件来说,我们最常见的配置如下:

include ':app'

那除此之外还能干什么呢?
其实,这里主要作用是用来定义这个工程有哪些module,并且定义其ProjectName以及对应的工程目录。那这里我们可以用来在整个工程的Project配置装载前做一些初始化的事情,例如生成动态module、配置构建缓存、配置module对应的BuildFile、插件冲突配置等。
具体能应用的配置可以查看org.gradle.api.initialization.Settings公开的API,每一个方法对应的都是一种配置选项。
例如,include ':app'对应的是void include(String... projectPaths)方法,而project(':annotation')对应的是ProjectDescriptor project(String path)方法,更多的脚本配置可以自行查询。
另外,这里也能applyGradle插件,会在后续的插件文章中详细说明,至此Settings就先到此为止了。

Project

对于每一个在settings.gradle中用include进行申明配置的module都对应一个Project实例,而对project而已,相应的构建配置是在对应项目根目录下的build.gradle中进行申明。

build.gradle

虽然配置文件中闭包配置对应都是同一个org.gradle.api.Project中的API,但是因为不同project所需要applygradle plugin会有所不同,因此导致看起来配置会稍有差异,尤其rootProjectproject,但是根源本质是一致的。
我们先从接触最多的project配置文件开始着手

apply plugin: 'com.android.application'

android {
    compileSdkVersion 27
    defaultConfig {
        applicationId "xx.yy.demo"
        minSdkVersion 14
        targetSdkVersion 27
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        debug {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:27.1.1'
}

这是最常见的一个配置,简单但是功能齐全,接着我们来根据配置从上到下分析gradle到底干了什么,配置的选项在编译过程中起什么作用。

  1. 首先调用projectvoid apply(Map<String, ?> options)方法把com.android.build.gradle.AppPlugin插件进行装载,如果apply的是com.android.library则对应的是com.android.build.gradle.LibraryPlugin插件。
  2. 接下来是熟悉的andoird{ ... },这其实是上述提到的AppPlugin插件的extension配置,对应的是com.android.build.gradle.AppExtension,当然如果是LibraryPlugin插件则对应的是com.android.build.gradle.LibraryPlugin,闭包中的每一个配置选项都是对应相应extension类中的方法。
  3. 最后是dependencies{...},这里是配置当前module编译所需要依赖的第三方库,实际对应的是先getDependencies()获取org.gradle.api.artifacts.dsl.DependencyHandler对象,然后调用其Dependency add(String configurationName, Object dependencyNotation)方法对其中定义的依赖配置进行添加。

至此,所对应的配置会在configure project过程中调用其对应的方法进行配置装载,后续应用到编译过程中。
build.gradle闭包配置先到此为止,更多配置例如task ...等其实跟上述一样是调用Task task(String name)方法,接下来说说project常用的几个方法。

DependencyHandler

从上述我们得知,增加第三方库依赖是调用Dependency add(String configurationName, Object dependencyNotation)方法,其中的apiimplementationruntimeOnly等其实是AppPlugin等插件中预先声明的configurationName,而dependencyNotation进行添加后则返回对应的实例对象。例如:

  1. fileTree(dir: 'libs', include: ['*.jar'])---org.gradle.api.internal.artifacts.dependencies.DefaultSelfResolvingDependency
  2. project(':xxxx')---org.gradle.api.internal.artifacts.dependencies.DefaultProjectDependency
  3. 'com.android.support:appcompat-v7:27.1.1'---org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency

因此,我们也可以在自己的插件中自定义configurationName,例如:


project.beforeEvaluate {

    Configuration customConfiguration = it.configurations.create("configurationName")
    customConfiguration.allDependencies.all{dependency ->
        //dependency进行处理
        ...
    }
}

beforeEvaluate / afterEvaluate

这两个方法是project最常用的两个方法:

  1. beforeEvaluate是设置project进行evaluate也就是对build.gradle进行配置加载之前的回调,适合apply plugin、定义Configuration等场景。
  2. afterEvaluate则是project进行配置加载之后的回调,适合需要根据配置的信息来进行操作的场景,因为在build.gradle中配置的extension要在afterEvaluate才能拿到。

hasProperty / getProperties

从方法名就可以猜到,是对project定义的属性数据进行操作,也就是闭包配置

ext{
  key = "value"
}

好了,project就先介绍到这了,更多的方法可以自行查询API和对应的注释信息。

后记

projectsettings的介绍就先到这了,这是gradle开发的基础,了解清楚就相当于迈过了入门的门槛石,随节会对其中的task进行深入介绍。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容