美文网首页
Android Plugin源码与Gradle构建(二)

Android Plugin源码与Gradle构建(二)

作者: AndroidHint | 来源:发表于2018-08-25 23:58 被阅读0次

一、前言

上一篇文章中我们讲到了Android Plugin中的apply方法中最为重要的三个回调方法,分别是configureProject、configureExtension、createTasks。而且还分析了configureProject回调方法中的逻辑,详见Android Plugin源码与Gradle构建(一)。今天我们就接着上面的思路,继续分析Android Plugin的源码。

二、初识Extension

我们先来看一段熟悉的build.gradle脚本:

android {
    compileSdkVersion 27
    defaultConfig {
        applicationId "com.example.runningh.mydemo"
        minSdkVersion 16
        targetSdkVersion 27
        versionCode 1
        versionName "1.0"
    }

    signingConfigs {
        release {
            storeFile file(release_storeFile)
            storePassword release_storePassword
            keyAlias release_keyAlias
            keyPassword release_keyPassword
        }
    }

    dataBinding {
        enabled true
    }

    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

相信大家对上述的build.gradle脚本应该很熟悉了。它是使用groovy语言编写的,上面这段脚本配置了项目的minSdkVersion(项目最低可运行在哪个版本的SDK手机上)、targetSdkVersion(项目已经在哪个版本的SDK上做了优化)、versionCode(版本号)、versionName(版本名)、signingConfigs(签名)、buildTypes(构建配置)等信息。
然而,为什么要这样写呢?我们自定义一个插件(关于自定义插件,可以参考这篇文章在AndroidStudio中自定义Gradle插件)叫做MyPlugin:

class MyPlugin implements Plugin<Project>{   
    @Override   
    void apply(Project project) {        
         project.extensions.add("book", Book)
         project.task('bookInfo') {
            group 'myplugin'
            doLast {
             Book ext = project.book
             println ext 
          }
       }
  }  
}  

apply方法中的第一行调用了project.extensionsadd方法将Book这个类加入到projectextension中,通过“book"(记住这个名字,后面需要在build.gradle中用到)这个名字可以引用到Book类。projectextension可以看做是一个容器。
apply方法的第二行定义了一个名为“bookInfo”的task,并将其放入到了名为“myplugin”的group中,然后该任务最后执行了将该Book类信息打印出来的逻辑。
我们看一下Book类是怎么定义的:

public class Book {
 
    String bookName;
 
    int price;
 
    @Override
    public String toString() {
        return "BookName is $bookName, price is $price."
    }
}

很简单,定义了两个变量,分别为bookName、price,并重写了toString方法,所以上面的MyPlugin插件直接调用println方法可直接执行BooktoString方法。
最后我们需要项目的build.gradle中配置book(还记得这个名字吗?我们通过project.extensionadd方法将该名字作为其中的一个参数):

book {
  bookName 'Android开发’
  price 100
}

看到这里,是不是和上面的android标签相类似呢?其实这种配置方法相当于给book做了初始化,相当于我们可以动态配置Book类的变量。最后我们执行一下bookInfo任务,执行结果如下:

BookName is Android开发, price is 100.

三、Android插件的Extension

private void configureExtension() {
    ObjectFactory objectFactory = project.getObjects();
    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();

    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);

    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);

    registerModels(registry, globalScope, variantManager, extension, extraModelInfo);

    // map the whenObjectAdded callbacks on the containers.
    signingConfigContainer.whenObjectAdded(variantManager::addSigningConfig);

    buildTypeContainer.whenObjectAdded(
            buildType -> {
                SigningConfig signingConfig =
                        signingConfigContainer.findByName(BuilderConstants.DEBUG);
                buildType.init(signingConfig);
                variantManager.addBuildType(buildType);
            });

    productFlavorContainer.whenObjectAdded(variantManager::addProductFlavor);

    // map whenObjectRemoved on the containers to throw an exception.
    signingConfigContainer.whenObjectRemoved(
            new UnsupportedAction("Removing signingConfigs is not supported."));
    buildTypeContainer.whenObjectRemoved(
            new UnsupportedAction("Removing build types is not supported."));
    productFlavorContainer.whenObjectRemoved(
            new UnsupportedAction("Removing product flavors is not supported."));

    // create default Objects, signingConfig first as its used by the BuildTypes.
    variantFactory.createDefaultComponents(
            buildTypeContainer, productFlavorContainer, signingConfigContainer);
}

调用project.container方法创建了一个类型为NamedDomainObjectContainer< BuildType >的对象buildTypeContainer,查看Gradle的API文档,可以看到container方法的作用:

大概的意思是创建了一个容器来管理泛型中定义的类,这些类可以使用factory工厂来创建。
所以上面创建了buildTypeproductFlavorsigningConfigBaseVariantOutput这四个类的容器。

然后调用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,名为“android”,类型为AppExtension,传递的参数有project、androidBuildersourceSetManager、extraModleInfo和上面创建的四个Container对象。
如果我们再回过头来看最上面我们提到的build.gradle中的android标签,其实android标签就对应着这里的“android”的Extension,然后android标签中可以配置compileSdkVersion,并且嵌套了defaultConfig、buildTypes、signingConfigs等标签,这些标签就有上面提到的四个Container对象。

最后还创建了variantFactory、taskManager、variantManager,其中variantFactory为构建信息的工厂、taskManager为构建任务管理、variantManager构建方式与多渠道构建管理。

四、总结

本文讲述了Android Plugin中的Extension,通过源码追踪的方式讲述了android标签、标签中的嵌套标签是怎么实现的,让我们在看build.gradle的配置信息时不至于一脸懵逼。

接下来的一篇文章,我们将讲到Android Plugin中的createTasks回调方法的逻辑。

相关文章

网友评论

      本文标题:Android Plugin源码与Gradle构建(二)

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