本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布

系列文章: 

从Android Plugin源码开始彻底理解gradle构建:初识AndroidDSL(一)

一、前言回顾

上回我们说到Android plugin的所有自定义plugin都需要重写的apply方法,并讲到该方法里最为重要的三个回调方法,我们回顾一下:

            //plugin的基础设置、初始化工作
            threadRecorder.record(  
                    ExecutionType.BASE_PLUGIN_PROJECT_CONFIGURE,  
                    project.getPath(),  
                    null,  
                    this::configureProject);  
            //EXTENSION的初始化工作
            threadRecorder.record(  
                    ExecutionType.BASE_PLUGIN_PROJECT_BASE_EXTENSION_CREATION,  
                    project.getPath(),  
                    null,  
                    this::configureExtension);  
             //plugin的task创建
            threadRecorder.record(  
                    ExecutionType.BASE_PLUGIN_PROJECT_TASKS_CREATION,  
                    project.getPath(),  
                    null,  
                    this::createTasks);

上一篇文章我们简单讲解第一个初始化方法,今天我们的主角就是第二个configureExtension方法

Extension究竟是什么?就我理解,可以把gradle的Extension比喻为JAVA的成员变量。虽然不完全相同,但是也有相似之处,何出此言?让我们继续往下看。

二、Extension介绍

讲解之前我们先看一段熟悉的代码:

android {
    compileSdkVersion 26
    defaultConfig {
        applicationId "com.example.gradletest"
        minSdkVersion 15
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

相信这段代码在座的各位都看吐了吧?

是的,笔者也一样。刚开始接触AS的时候,看这段代码感觉就感觉在看天书,反正按规定配置就对了。今天我们就把他扒光了看看有什么特别。

仔细观察其实也比较好理解,就是android的一些配置:

compileSdkVersion指的是当前的androidSDK版本、applicationId指的是包名、versionCode指的是版本号….balabala…

虽然经过几年的百度、谷歌熏陶已经很熟悉了,但是为什么这么配置呢?接下来就为各位看官解答。

我们可以把一个gradle项目看做一个Project,而这个Project就类似JAVA的类,而一个类自然就需要成员变量咯,所以我们首先就来创建一个pojo类,就命名为Person吧:

public class Person {

    String name;

    int age;

    @Override
    public String toString() {
        return "I am $name, $age years old"
    }
}

然后就像上篇文章那样创建一个自定义插件:

class MyPlugin implements Plugin<Project>{   
    @Override   
    void apply(Project project) {        
         project.extensions.add("person", Person)
         project.task('printPerson') {
            group 'atom'
            doLast{
             Person ext = project.person
             println ext 
          }
       }
  }  
}

第一句调用了project的extensions,他可以视为变量的容器,只要往里加就OK了,然后就可以通过project调用到person了

第二句是我们下节需要讲到的内容,可以暂时无视,直接看dolast里面的代码就好了

很简单,我直接打印了该person,接下来就是重点了,我们需要在build.gradle里面配置person

person{
    name  'atom'
    age  18
}

是不是就像android的标签一样?这下应该很好理解gradle里为何这么写了吧。

其实这里就相当于我们给person做了初始化,只是用有些像json的写法而已。

不过这样我们就可以配置项目参数了,是不是很方便?

我们来验证一下,执行一下gradle printPerson命令:

如我们所想,打印了我们配置的18岁的atom:)

三、Android的Extension

有了基础的认识,让我们回到文章开头的configureExtension方法:
    private void configureExtension() {
        ObjectFactory objectFactory = project.getObjects();
        //1==============
        final NamedDomainObjectContainer<BuildType> buildTypeContainer =
                project.container(
                        BuildType.class,
                        new BuildTypeFactory(
                                objectFactory,
                                project,
                                extraModelInfo.getSyncIssueHandler(),
                                extraModelInfo.getDeprecationReporter()));
        final NamedDomainObjectContainer<ProductFlavor> productFlavorContainer =
                project.container(
                        ProductFlavor.class,
                        new ProductFlavorFactory(
                                objectFactory,
                                project,
                                extraModelInfo.getDeprecationReporter(),
                                project.getLogger()));
        final NamedDomainObjectContainer<SigningConfig> signingConfigContainer =
                project.container(
                        SigningConfig.class,
                        new SigningConfigFactory(
                                objectFactory,
                                GradleKeystoreHelper.getDefaultDebugKeystoreLocation()));

        final NamedDomainObjectContainer<BaseVariantOutput> buildOutputs =
                project.container(BaseVariantOutput.class);

        project.getExtensions().add("buildOutputs", buildOutputs);

        sourceSetManager = createSourceSetManager();
        //2==============
        extension =
                createExtension(
                        project,
                        projectOptions,
                        androidBuilder,
                        sdkHandler,
                        buildTypeContainer,
                        productFlavorContainer,
                        signingConfigContainer,
                        buildOutputs,
                        sourceSetManager,
                        extraModelInfo);


        ndkHandler =
                new NdkHandler(
                        project.getRootDir(),
                        null, /* compileSkdVersion, this will be set in afterEvaluate */
                        "gcc",
                        "" /*toolchainVersion*/,
                        false /* useUnifiedHeaders */);




        @Nullable
        FileCache buildCache = BuildCacheUtils.createBuildCacheIfEnabled(project, projectOptions);


        GlobalScope globalScope =
                new GlobalScope(
                        project,
                        projectOptions,
                        androidBuilder,
                        extension,
                        sdkHandler,
                        ndkHandler,
                        registry,
                        buildCache);

        //3===============================
        variantFactory = createVariantFactory(globalScope, androidBuilder, extension);


        taskManager =
                createTaskManager(
                        globalScope,
                        project,
                        projectOptions,
                        androidBuilder,
                        dataBindingBuilder,
                        extension,
                        sdkHandler,
                        ndkHandler,
                        registry,
                        threadRecorder);


        variantManager =
                new VariantManager(
                        globalScope,
                        project,
                        projectOptions,
                        androidBuilder,
                        extension,
                        variantFactory,
                        taskManager,
                        sourceSetManager,
                        threadRecorder);

        //省略部分代码
        // create default Objects, signingConfig first as its used by the BuildTypes.
        variantFactory.createDefaultComponents(
                buildTypeContainer, productFlavorContainer, signingConfigContainer);
    }


1、首先创建了四个NamedDomainObjectContainer,是由project的Container方法返回的,这些东东是什么有什么用,光靠百度谷歌基本上是很难找到了(笔者写这篇文章的时候gradle方面的资料还是相当匮乏的),所以我们得学会看官方文档咯~


也不算太难,笔者这二流英语水平都能看懂:创建一个容器,用来管理泛型中定义的类。而factory自然就是创建该类的工厂了。

所以根据上诉代码,不难知道创建了BuildType、ProductFlavor、SigningConfig、BaseVariantOutput这四个类的容器了。

稍微熟悉构建的童鞋应该很清楚这几个类的用处:

buildType

构建类型,在Android Gradle工程中,它已经帮我们内置了debug和release两个构建类型,可以分别设置不同包名等信息。

signingConfigs

签名配置,可以设置debug和release甚至自定义方式时的不同keystore,及其密码等信息。

ProductFlavor

多渠道打包必备,用处很多笔者也有推荐文章介绍

而最后一个BaseVariantOutput,“望文生义“不难才到就是输出文件咯~

2、把project、androidBuilder以及刚刚提到的几个类作为参数创建了extension,这里使用了策略模式,createExtension是一个抽象方法,真正实现是在AppPlugin

    protected BaseExtension createExtension(
            @NonNull Project project,
            @NonNull ProjectOptions projectOptions,
            @NonNull AndroidBuilder androidBuilder,
            @NonNull SdkHandler sdkHandler,
            @NonNull NamedDomainObjectContainer<BuildType> buildTypeContainer,
            @NonNull NamedDomainObjectContainer<ProductFlavor> productFlavorContainer,
            @NonNull NamedDomainObjectContainer<SigningConfig> signingConfigContainer,
            @NonNull NamedDomainObjectContainer<BaseVariantOutput> buildOutputs,
            @NonNull SourceSetManager sourceSetManager,
            @NonNull ExtraModelInfo extraModelInfo) {
        return project.getExtensions()
                .create(
                        "android",
                        AppExtension.class,
                        project,
                        projectOptions,
                        androidBuilder,
                        sdkHandler,
                        buildTypeContainer,
                        productFlavorContainer,
                        signingConfigContainer,
                        buildOutputs,
                        sourceSetManager,
                        extraModelInfo);
    }

就如同我们之前介绍创建Extension的方式一样,通过project创建了名为“android”的Extension,类型为AppExtension,这个类就包含了我们平时用到的版本号、包名等等信息,为我们构建项目打下了基础。

3、用同样的方式创建了variantFactory、taskManager、variantManager,最后设置了默认的构建信息。

关于这几个类的作用分别是:

variantFactory构建信息的工厂、taskManager构建任务、variantManager各种不同构建方式及多渠道构建的管理

这就涉及到gradle核心:task了,也是下一篇文章需要讲的内容,大家敬请关注。

四、结语

本文主要介绍了build中配置信息写法的由来,让大家看到配置信息时不再那么没有底气。

不知道大家有没有发现,我例子中使用的groovy语法来写插件,而Android Plugin确是使用Java来写的

具体原因不明,但是可以看出,groovy是完全兼容Java的,如果你想的话甚至可以使用kotlin来写,最新版本的AndroidDSL也加入了kotlin代码。但是还是推荐学习groovy的基本用法,毕竟也有喜欢使用groovy来写插件的,我们得看的懂对吧,比如virtualAPK~

Categories: gradle

4 Comments

Tutusa · 2019年4月3日 at 上午12:52

Today anybody has an Android device all across the world. This has lead to developing a tons of apks that provide servises such as mobile games, entertainment and others. To download this apps a user must get an account at Google Store. But not all programs are available there as they don’t meet the Android rules. To combat this a new app was developed named TutuApp. This helper works on Android run phones and tablets and it has the biggest store of APK games. We name a few: Clash of Clans, Pokemon and many others. They come with zero ads and locked content! Whant to know more? Visit https://tutuapp-app.com/apk/ to download TutuApp APK for free on your Android phone. Installation instructions for TutuApp Helper are available there.

Vidmsa · 2019年4月27日 at 上午7:21

Today almost everyone uses Youtube or any other video hosting provider to upload and watch funny videos, free movies and other stuff. But sometimes you want to download a video or a movie and the service doesn’t allow you to do so. That’s where Vidmate is coming to help you. It helps you to download a movie from may services, including Youtube, Vimeo, Facebook and many other hosting services. To perform this you just need to open video page and click “download”. Plus, the application is completely free of charge and very easy in use! Download Vidmate app now and start downloading your favourite movies to share them with your family and friends!

Bryantget · 2019年4月28日 at 上午8:41

Hi, veryatom.com

I’ve been visiting your website a few times and decided to give you some positive feedback because I find it very useful. Well done.

I was wondering if you as someone with experience of creating a useful website could help me out with my new site by giving some feedback about what I could improve?

You can find my site by searching for “casino gorilla” in Google (it’s the gorilla themed online casino comparison).

I would appreciate if you could check it out quickly and tell me what you think.

casinogorilla.com

Thank you for help and I wish you a great week!

Timothyhaift · 2019年5月15日 at 下午12:04

Hi superioroffers
Decent click on the vinculum less to prepare

https://drive.google.com/file/d/1lJKSO-jI7n2Nz5Hg9fY49nBOts4dt7ak/preview

发表评论

电子邮件地址不会被公开。