Gradle随记---初识

2019-02-01  本文已影响0人  Kael_Huang

谈起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进行深入介绍。

上一篇下一篇

猜你喜欢

热点阅读