Gradle插件

一、插件相关API

PluginAware主要定义了插件相关API。

public interface PluginAware {
    PluginContainer getPlugins();
    void apply(Closure closure);
    void apply(Action< ? super ObjectConfigurationAction> action);
    void apply(Map<String, ?> options);
    PluginManager getPluginManager();
}

应用插件

apply plugin: 'java',表示应用Java插件。这个语句调用了apply()方法,后面的plugin: 'java'是一个Map类型参数。

apply plugin: MyClass表示应用指定class实现的插件,将在后面的Plugin中介绍。

org.gradle.api.Plugin

Plugin用于定义插件。Gradle提供了完整的API框架,而很多工作实际是由插件实现的。Gradle内置了Java、Groovy等几种基础插件,也可以自定义插件。

Plugin接口很简单,只有一个apply方法。

public interface Plugin<T> {
    /**
     * Apply this plugin to the given target object.
     *
     * @param target The target object
     */
    void apply(T target);
}

二、简易插件开发

下面的示例代码实现了HelloPlugin的简易插件,代码可直接写在build.gradle中。

插件在apply(Project)方法里,给Project创建了一个名为hello的Extension和一个名为welcome的Task;Task执行时读取Extension并打印字符串。

build.gradle执行到apply plugin: HelloPlugin时,HelloPlugin.apply(Project)方法被执行,从而Project有了hello的Extension,于是后面可以调用hello {}对插件进行配置。

class HelloExtension {
    Boolean enable = true
    String text = ''
}
class HelloPlugin implements Plugin<Project> {
    @Override
    void apply(Project project) {
        project.extensions.create('hello', HelloExtension)
        project.task('welcome') {
            doLast {
                HelloExtension ext = project.extensions.hello;
                println ext.enable ? "Hello ${ext.text}!" : 'HelloPlugin is disabled.'
            }
        }
    }
}
apply plugin: HelloPlugin
hello {
    enable = true
    text = 'Gradle'
}

在命令行中执行结果如下:

$ ./gradlew welcome
:welcome
Hello Gradle!
BUILD SUCCESSFUL

Android transform

android gradle plugin 提供了 transform api 用来在 .class to dex 过程中对 class 进行处理,可以理解为一种特殊的 Task,因为 transform 最终也会转化为 Task 去执行
要实现 transform 需要继承 com.android.build.api.transform.Transform 并实现其方法,实现了 Transform 以后,要想应用,就调用project.android.registerTransform()

public class MyTransform extends Transform {
    @Override
    public String getName() {
        // 返回 transform 的名称,最终的名称会是 transformClassesWithMyTransformForDebug 这种形式   
        return "MyTransform";
    }

    @Override
    public Set<QualifiedContent.ContentType> getInputTypes() {
        /**
        返回需要处理的数据类型 有 下面几种类型可选
        public static final Set<ContentType> CONTENT_CLASS = ImmutableSet.of(CLASSES);
        public static final Set<ContentType> CONTENT_JARS = ImmutableSet.of(CLASSES, RESOURCES);
        public static final Set<ContentType> CONTENT_RESOURCES = ImmutableSet.of(RESOURCES);
        public static final Set<ContentType> CONTENT_NATIVE_LIBS = ImmutableSet.of(NATIVE_LIBS);
        public static final Set<ContentType> CONTENT_DEX = ImmutableSet.of(ExtendedContentType.DEX);
        public static final Set<ContentType> DATA_BINDING_ARTIFACT = ImmutableSet.of(ExtendedContentType.DATA_BINDING);
        */
        return TransformManager.CONTENT_CLASS;
    }

    @Override
    public Set<? super QualifiedContent.Scope> getScopes() {
        /**
        返回需要处理内容的范围,有下面几种类型
        PROJECT(1), 只处理项目的内容
        SUB_PROJECTS(4), 只处理子项目
        EXTERNAL_LIBRARIES(16), 只处理外部库
        TESTED_CODE(32), 只处理当前 variant 对应的测试代码
        PROVIDED_ONLY(64), 处理依赖
        @Deprecated
        PROJECT_LOCAL_DEPS(2),
        @Deprecated
        SUB_PROJECTS_LOCAL_DEPS(8);
        */
        return Sets.immutableEnumSet(QualifiedContent.Scope.PROJECT);
    }

    @Override
    public boolean isIncremental() {
        // 是否增量,如果返回 true,TransformInput 会包括一份修改的文件列表,返回 false,会进行全量编译,删除上一次的输出内容
        return false;
    }

    @Override
    void transform(TransformInvocation transformInvocation) throws TransformException, InterruptedException, IOException {
        // 在这里处理 class
        super.transform(transformInvocation)
        // 在 transform 里,如果没有任何修改,也要把 input 的内容输出到 output,否则会报错
        for (TransformInput input : transformInvocation.inputs) {
            input.directoryInputs.each { dir ->
                // 获取对应的输出目录
                File output = transformInvocation.outputProvider.getContentLocation(dir.name, dir.contentTypes, dir.scopes, Format.DIRECTORY)
                dir.changedFiles // 增量模式下修改的文件
                dir.file // 获取输入的目录
                FileUtils.copyDirectory(dir.file, output) // input 内容输出到 output
            }
            input.jarInputs.each { jar ->
                // 获取对应的输出 jar
                File output = transformInvocation.outputProvider.getContentLocation(jar.name, jar.contentTypes, jar.scopes, Format.JAR)
                jar.file // 获取输入的 jar 文件
                FileUtils.copyFile(jar.file, output) // input 内容输出到 output
            }
        }
    }
}

// 注册 transform
android.registerTransform(new MyTransform())

三、插件开发

  1. 在 android studio 中创建一个 java module

  2. 在 src/main 目录下创建 groovy 目录,然后创建自己的包名和插件类

  3. 在 src/main 目录下创建 resources/META-INFO/gradle-plugins 目录,创建 myplugin.properties 文件,文件里内容是

    implementation-class=com.*.MyPlugin // 这里是自己的插件类
    
  4. 修改 build.gradle 文件

    // 引入 groovy 和 java 插件
    apply plugin: 'groovy'
    apply plugin: 'java'
    
    buildscript {
        repositories {
            mavenLocal()
            maven { url 'https://maven.google.com' }
            jcenter()
        }
    }
    
    repositories {
        mavenLocal()
        maven { url 'https://maven.google.com' }
    }
    
    dependencies {
        compile gradleApi()
        compile localGroovy()
        compile 'com.android.tools.build:gradle:3.0.1'
    }
    
  1. 创建Plugin类

    创建插件类,就可以写插件的代码了。插件类继承 Plugin,并实现 apply 接口,apply 就是在 build.gradle 里 apply plugin 'xxx' 的时候要调用的接口了
    插件开发可以使用 groovy 和 java,使用 groovy 的话可以有更多的语法糖,开发起来更方便一些

    package com.binzi.plugin
    
    import org.gradle.api.Plugin
    import org.gradle.api.Project
    
    class MyPlugin implements Plugin<Project> {
    
        @Override
        void apply(Project project) {
            println("apply my plugin")
        }
    }
    
  2. 创建插件的task

    我们再定义一个 task 类 MyTask,继承自 DefaultTask,简单的输出一些信息

    package com.binzi.plugin
    
    import org.gradle.api.DefaultTask
    import org.gradle.api.tasks.TaskAction
    
    class MyTask extends DefaultTask {
    
        @TaskAction
        void action() {
            println('my task run')
        }
    }
    

    然后在 plugin 中注册这个 task

    class MyPlugin implements Plugin<Project> {
    
        @Override
        void apply(Project project) {
            println("apply my plugin")
            project.tasks.create("mytask", MyTask.class)
        }
    }
    
  3. 本地安装插件

    这样一个简单的插件就开发好了,如何使用呢
    我们首先需要在 build.gradle 中引入 maven 插件,并且配置 install 相关的属性

    apply plugin: 'maven'
    
    install {
        repositories.mavenInstaller {
            pom.version = '0.0.1' // 配置插件版本号
            pom.artifactId = 'myplugin' // 配置插件标识
            pom.groupId = 'com.binzi.plugin' // 配置插件组织
        }
    }
    

    之后执行 ./gradlew install 便会把插件安装在本地 maven 仓库
    之后在使用的地方引入我们插件的 classpath

    classpath 'com.binzi.plugin:myplugin:0.0.1'
    

    加载插件

    apply plugin; 'myplugin' // 这里的 myplugin 是前面说的 myplugin.properties 的名字
    
  4. 打包发布

    在插件 build.gradle 里新增上传的配置如下

    uploadArchives {
        repositories {
            mavenDeployer {
                repository(url: "mavenUrl")
                pom.version = '0.0.1'
                pom.artifactId = 'myplugin'
            }
        }
    }
    

Android Plugin主要流程

插件入口

在前面讲解自定义插件的时候说到过,要定义一个 xxx.properties 文件,里面声明插件的入口类,要知道 android gradle plugin 的入口类,看源码中android.properties 文件就可以,内容如下:

implementation-class=com.android.build.gradle.AppPlugin

这里定义了入口是 AppPlugin,AppPlugin 继承自AbstractAppPlugin,AbstractAppPlugin继承自BasePlugin。
AbstractAppPlugin 里没有做太多的操作,主要是重写了 createTaskManager 和 createExtension,剩下的大部分工作还是在 BasePlugin 里做的。

BasePlugin中的的入口函数是apply(0

 @Override
 public final void apply(@NonNull Project project) {
     CrashReporting.runAction(
         () -> {
         basePluginApply(project);
         pluginSpecificApply(project);
     });
 }

这里主要调用

 @Override
 public final void apply(@NonNull Project project) {
     CrashReporting.runAction(
         () -> {
         basePluginApply(project);
         pluginSpecificApply(project);
     });
 }

这里主要调用

 @Override
 public final void apply(@NonNull Project project) {
     CrashReporting.runAction(
         () -> {
         basePluginApply(project);
         pluginSpecificApply(project);
     });
 }

这里主要调用basePluginApply和pluginSpecificApply,其中pluginSpecificApply是个抽象函数,在pluginSpecificApply中是个空实现,主要操作都在basePluginApply中。

下面来看看basePluginApply中都干了些啥:

//检查工程路径
checkPathForErrors();
//检查两个module是否有相同的id
checkModulesForErrors();
//PluginInitializer初始化
PluginInitializer.initialize(project);
//ProfilerInitializer初始化
ProfilerInitializer.init(project, projectOptions);
threadRecorder = ThreadRecorder.get();

// 使用project的选项配置worker
Workers.INSTANCE.initFromProject(
projectOptions,
ForkJoinPool.commonPool());
//profiler中写入plugin信息
ProcessProfileWriter.getProject(project.getPath())
    .setAndroidPluginVersion(Version.ANDROID_GRADLE_PLUGIN_VERSION)
    .setAndroidPlugin(getAnalyticsPluginType())
    .setPluginGeneration(GradleBuildProject.PluginGeneration.FIRST)
    .q(AnalyticsUtil.toProto(projectOptions));

 if (!projectOptions.get(BooleanOption.ENABLE_NEW_DSL_AND_API)) {
    //配置project
     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);
    //创建task
     threadRecorder.record(
         ExecutionType.BASE_PLUGIN_PROJECT_TASKS_CREATION,
         project.getPath(),
         null,
         this::createTasks);
 }

配置Project

private void configureProject() {
    final Gradle gradle = project.getGradle();
    ObjectFactory objectFactory = project.getObjects();
    //配置项对应的model
    extraModelInfo = new ExtraModelInfo(project.getPath(), projectOptions, project.getLogger());

    sdkHandler = new SdkHandler(project, getLogger());
    if (!gradle.getStartParameter().isOffline()
        && projectOptions.get(BooleanOption.ENABLE_SDK_DOWNLOAD)) {
        SdkLibData sdkLibData = SdkLibData.download(getDownloader(), getSettingsController());
        sdkHandler.setSdkLibData(sdkLibData);
    }
    //创建AndroidBuilder
    AndroidBuilder androidBuilder =
        new AndroidBuilder(
            project == project.getRootProject() ? project.getName() : project.getPath(),
            creator,
            new GradleProcessExecutor(project),
            new GradleJavaProcessExecutor(project),
            extraModelInfo.getSyncIssueHandler(),
            extraModelInfo.getMessageReceiver(),
            getLogger());
    dataBindingBuilder = new DataBindingBuilder();
    dataBindingBuilder.setPrintMachineReadableOutput(
        SyncOptions.getErrorFormatMode(projectOptions) == ErrorFormatMode.MACHINE_PARSABLE);

    if (projectOptions.hasRemovedOptions()) {
        androidBuilder
        .getIssueReporter()
        .reportWarning(Type.GENERIC, projectOptions.getRemovedOptionsErrorMessage());
    }

    if (projectOptions.hasDeprecatedOptions()) {
        extraModelInfo
        .getDeprecationReporter()
        .reportDeprecatedOptions(projectOptions.getDeprecatedOptions());
    }

    if (!projectOptions.getExperimentalOptions().isEmpty()) {
        projectOptions
        .getExperimentalOptions()
        .forEach(extraModelInfo.getDeprecationReporter()::reportExperimentalOption);
    }

    // Enforce minimum versions of certain plugins
    GradlePluginUtils.enforceMinimumVersionsOfPlugins(
        project, androidBuilder.getIssueReporter());

    //应用java插件
    project.getPlugins().apply(JavaBasePlugin.class);

    DslScopeImpl dslScope =
        new DslScopeImpl(
            extraModelInfo.getSyncIssueHandler(),
            extraModelInfo.getDeprecationReporter(),
            objectFactory);
    //创建build缓存文件
    @Nullable
    FileCache buildCache = BuildCacheUtils.createBuildCacheIfEnabled(project, projectOptions);

    globalScope =
        new GlobalScope(
            project,
            new ProjectWrapper(project),
            projectOptions,
            dslScope,
            androidBuilder,
            sdkHandler,
            registry,
            buildCache);

    project.getTasks()
    .getByName("assemble")
    .setDescription(
        "Assembles all variants of all applications and secondary packages.");

    // 添加build监听
    gradle.addBuildListener(
        new BuildListener() {
            @Override
            public void buildStarted(@NonNull Gradle gradle) {}

            @Override
            public void settingsEvaluated(@NonNull Settings settings) {}

            @Override
            public void projectsLoaded(@NonNull Gradle gradle) {}

            @Override
            public void projectsEvaluated(@NonNull Gradle gradle) {}

            @Override
            public void buildFinished(@NonNull BuildResult buildResult) {
                // Do not run buildFinished for included project in composite build.
                if (buildResult.getGradle().getParent() != null) {
                    return;
                }
                ModelBuilder.clearCaches();
                sdkHandler.unload();
                threadRecorder.record(
                    ExecutionType.BASE_PLUGIN_BUILD_FINISHED,
                    project.getPath(),
                    null,
                    () -> {
                        WorkerActionServiceRegistry.INSTANCE
                        .shutdownAllRegisteredServices(
                            ForkJoinPool.commonPool());
                        Main.clearInternTables();
                    });
                DeprecationReporterImpl.Companion.clean();
            }
        });

    createLintClasspathConfiguration(project);
}

配置Extension

private void configureExtension() {
    ...
        
    extension = createExtension(
                    project,
                    projectOptions,
                    globalScope,
                    sdkHandler,
                    buildTypeContainer,
                    productFlavorContainer,
                    signingConfigContainer,
                    buildOutputs,
                    sourceSetManager,
                    extraModelInfo);
    
    taskManager = createTaskManager(
                    globalScope,
                    project,
                    projectOptions,
                    dataBindingBuilder,
                    extension,
                    sdkHandler,
                    variantFactory,
                    registry,
                    threadRecorder);
    
    ...
     variantFactory.createDefaultComponents(
                buildTypeContainer, productFlavorContainer, signingConfigContainer);
}

其中主要是调用 createExtension来加载gradle脚本中的配置信息,在使用解析完后的配置信息创建taskManager、variantManager等其他组件。

创建 task

    private void createTasks() {
        threadRecorder.record(
                ExecutionType.TASK_MANAGER_CREATE_TASKS,
                project.getPath(),
                null,
                () -> taskManager.createTasksBeforeEvaluate());

        project.afterEvaluate(
                CrashReporting.afterEvaluate(
                        p -> {
                            sourceSetManager.runBuildableArtifactsActions();

                            threadRecorder.record(
                                    ExecutionType.BASE_PLUGIN_CREATE_ANDROID_TASKS,
                                    project.getPath(),
                                    null,
                                    this::createAndroidTasks);
                        }));
    }

createAndroidTasks 的调用时机是在 project.afterEvaluate 里调用的,这个时候所有模块配置已经完成了
在 BasePlugin.createAndroidTasks 里,是调用 VariantManager.createAndroidTasks 完成工作的。

四、Android Plugin主要 Task

1、Android 打包流程

官方介绍的流程如下:

  1. 编译器将您的源代码转换成 DEX(Dalvik Executable) 文件(其中包括 Android 设备上运行的字节码),将所有其他内容转换成已编译资源。
  2. APK 打包器将 DEX 文件和已编译资源合并成单个 APK。 不过,必须先签署 APK,才能将应用安装并部署到 Android 设备上。
  3. APK 打包器使用调试或发布密钥库签署您的 APK:
  4. 在生成最终 APK 之前,打包器会使用 zipalign 工具对应用进行优化,减少其在设备上运行时占用的内存。

首先我们看一下 打包一个 apk 需要哪些 task。
在项目根目录下执行命令:

./gradlew assembleDebug --console=plain

看一下输出结果

> Task :app:preBuild UP-TO-DATE
> Task :app:prepareLintJar UP-TO-DATE
> Task :app:preDebugBuild
> Task :app:compileDebugAidl NO-SOURCE
> Task :app:compileDebugRenderscript NO-SOURCE
> Task :app:checkDebugManifest
> Task :app:generateDebugBuildConfig
> Task :app:generateDebugSources
> Task :app:javaPreCompileDebug
> Task :app:mainApkListPersistenceDebug
> Task :app:generateDebugResValues
> Task :app:generateDebugResources
> Task :app:mergeDebugResources
> Task :app:createDebugCompatibleScreenManifests
> Task :app:processDebugManifest
> Task :app:processDebugResources
> Task :app:compileDebugJavaWithJavac
> Task :app:compileDebugSources
> Task :app:mergeDebugShaders
> Task :app:compileDebugShaders
> Task :app:generateDebugAssets
> Task :app:mergeDebugAssets
> Task :app:checkDebugDuplicateClasses
> Task :app:transformClassesWithDexBuilderForDebug
> Task :app:validateSigningDebug
> Task :app:signingConfigWriterDebug
> Task :app:mergeExtDexDebug
> Task :app:mergeDexDebug
> Task :app:mergeDebugJniLibFolders
> Task :app:transformNativeLibsWithMergeJniLibsForDebug
> Task :app:transformNativeLibsWithStripDebugSymbolForDebug
> Task :app:processDebugJavaRes NO-SOURCE
> Task :app:transformResourcesWithMergeJavaResForDebug
> Task :app:packageDebug
> Task :app:assembleDebug

Task 对应实现类

我们先看看每个 task 都是做什么的,以及其对应的实现类。
先回忆一下,我们在前面 android-gradle-plugin 主要流程分析里说到过,task 的实现可以在 TaskManager 里找到,创建 Task 的方法主要是两个,TaskManager.createTasksBeforeEvaluate() 和 ApplicationTaskManager.createTasksForVariantScope(),所以这些 task 的实现,也在这两个类里找就可以,下面列出了各个 task 的作用及实现类。

Task 对应实现类 作用
preBuild 空 task,只做锚点使用
preDebugBuild 空 task,只做锚点使用,与 preBuild 区别是这个 task 是 variant 的锚点
compileDebugAidl AidlCompile 处理 aidl
compileDebugRenderscript RenderscriptCompile 处理 renderscript
checkDebugManifest CheckManifest 检测 manifest 是否存在
generateDebugBuildConfig GenerateBuildConfig 生成 BuildConfig.java
prepareLintJar PrepareLintJar 拷贝 lint jar 包到指定位置
generateDebugResValues GenerateResValues 生成 resvalues,generated.xml
generateDebugResources 空 task,锚点
mergeDebugResources MergeResources 合并资源文件
createDebugCompatibleScreenManifests CompatibleScreensManifest manifest 文件中生成 compatible-screens,指定屏幕适配
processDebugManifest MergeManifests 合并 manifest 文件
splitsDiscoveryTaskDebug SplitsDiscovery 生成 split-list.json,用于 apk 分包
processDebugResources ProcessAndroidResources aapt 打包资源
generateDebugSources 空 task,锚点
javaPreCompileDebug JavaPreCompileTask 生成 annotationProcessors.json 文件
compileDebugJavaWithJavac AndroidJavaCompile 编译 java 文件
compileDebugNdk NdkCompile 编译 ndk
compileDebugSources 空 task,锚点使用
mergeDebugShaders MergeSourceSetFolders 合并 shader 文件
compileDebugShaders ShaderCompile 编译 shaders
generateDebugAssets 空 task,锚点
mergeDebugAssets MergeSourceSetFolders 合并 assets 文件
transformClassesWithDexBuilderForDebug DexArchiveBuilderTransform class 打包 dex
transformDexArchiveWithExternalLibsDexMergerForDebug ExternalLibsMergerTransform 打包三方库的 dex,在 de增量的时候就不需要再 merge 了,节省时间
transformDexArchiveWithDexMergerForDebug DexMergerTransform 打包最终的 dex
mergeDebugJniLibFolders MergeSouceSetFolders 合并 jni lib 文件
transformNativeLibsWithMergeJniLibsForDebug MergeJavaResourcesTransform 合并 jnilibs
transformNativeLibsWithStripDebugSymbolForDebug StripDebugSymbolTransform 去掉 native lib 里的 debug 符号
processDebugJavaRes ProcessJavaResConfigAction 处理 java res
transformResourcesWithMergeJavaResForDebug MergeJavaResourcesTransform 合并 java res
validateSigningDebug ValidateSigningTask 验证签名
packageDebug PackageApplication 打包 apk
assembleDebug 空 task,锚点
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容