美文网首页
Gradle随记---初识

Gradle随记---初识

作者: Kael_Huang | 来源:发表于2019-02-01 18:10 被阅读0次

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

    相关文章

      网友评论

          本文标题:Gradle随记---初识

          本文链接:https://www.haomeiwen.com/subject/llsolqtx.html