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

Android Plugin源码与Gradle构建(一)

作者: AndroidHint | 来源:发表于2018-08-20 21:11 被阅读0次

    一、前言

    现在Android开发最常用的IDE就是Android Studio了。在Android Studio中使用了Gradle构建功能,这使得模块之间的管理、依赖都非常的方便清晰。

    同时,国内比较火热的Android插件化、热更新等都涉及到了Gradle插件的知识,熟练的掌握Gradle,可以让我们更加清楚的了解Android的构建过程,改造构建过程以达到某些功能需求。从这个角度来说,Android开发是需要掌握Gradle这项技能的。

    二、Android Plugin源码获取

    在Android项目的build.gradle中,有这样的一行代码:

    apply plugin: 'com.android.application'
    

    想必大家对这行代码都相当的熟悉吧。它要表达的意思是将名为com.android.application的插件运用到我们的项目中,这个插件就是大名鼎鼎的Android Plugin

    那么Android Plugin是怎么进入到我们的工程中的呢?


    在项目的根目录下的build.gradle中找到 classpath 对gradle插件的引用,如上图。Android Plugin就包含在上述的 gradle 插件中。

    我们将上面的gradle插件复制到项目的build.gradle中(注意:这里是项目的build.gradle,和上面的build.gradle不一样!)。

    同步(sync)一下项目,就可以在项目的依赖树中找到Android Plugin源码。

    三、Android Plugin源码解析

    展开上面的com.android.tools.build:gradle:3.1.0@jar,可以看到AppPlugin和LibraryPlugin,其中AppPlugin就是Android项目需要依赖的插件,而LibraryPlugin是组件项目需要依赖的插件

    下面我们就解析一下AppPlugin的源码,并从中了解App构建过程。Let's do it~

    首先AppPlugin继承了BasePlugin,BasePlugin实现了Plugin接口,并实现了其中的apply方法。apply方法作为BasePlugin的入口类,其实现如下:

    @Override
    public void apply(@NonNull Project project) {
        //初始化项,省略
    
        //构造线程记录器,用于记录执行时间
        threadRecorder = ThreadRecorder.get();
    
        ProcessProfileWriter.getProject(project.getPath())
                .setAndroidPluginVersion(Version.ANDROID_GRADLE_PLUGIN_VERSION)
                .setAndroidPlugin(getAnalyticsPluginType())
                .setPluginGeneration(GradleBuildProject.PluginGeneration.FIRST)
                .setOptions(AnalyticsUtil.toProto(projectOptions));
    
        BuildableArtifactImpl.Companion.disableResolution();
        if (!projectOptions.get(BooleanOption.ENABLE_NEW_DSL_AND_API)) {
            //不使用新的DSL API
            TaskInputHelper.enableBypass();
            // configureProject 配置项目
            threadRecorder.record(
                    ExecutionType.BASE_PLUGIN_PROJECT_CONFIGURE,
                    project.getPath(),
                    null,
                    this::configureProject);
            // configureExtension 配置 Extension,之后我们才能使用 android {} 进行配置
            threadRecorder.record(
                    ExecutionType.BASE_PLUGIN_PROJECT_BASE_EXTENSION_CREATION,
                    project.getPath(),
                    null,
                    this::configureExtension);
            // createTasks 创建必须的 task
            threadRecorder.record(
                    ExecutionType.BASE_PLUGIN_PROJECT_TASKS_CREATION,
                    project.getPath(),
                    null,
                    this::createTasks);
        } else {
            //省略
        }
    }
    

    上面的apply方法最重要的是调用了三次ThreadRecorder的record方法。注意:this::configureProject是java8 的lambda写法,所以接下来我们看一下record方法的实现:

    @Override
    public void record(
            @NonNull ExecutionType executionType,
            @NonNull String projectPath,
            @Nullable String variant,
            @NonNull VoidBlock block) {
        ProfileRecordWriter profileRecordWriter = ProcessProfileWriter.get();
        //创建GradleBuildProfileSpan.Builder对象
        GradleBuildProfileSpan.Builder currentRecord =
                create(profileRecordWriter, executionType, null);
        try {
            //回调方法
            block.call();
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        } finally {
            //将上面创建的GradleBuildProfileSpan对象写入到ProfileRecordWriter对象的span(队列)变量中
            write(profileRecordWriter, currentRecord, projectPath, variant);
        }
    }
    

    record方法主要完成了三个逻辑:
    1、创建了创建GradleBuildProfileSpan.Builder对象;
    2、执行回调方法;
    3、最后将GradleBuildProfileSpan对象写入到ProfileRecordWriter对象的span(队列)变量中。

    首先我们看1的逻辑,调用了create方法创建了GradleBuildProfileSpan.Builder对象:

    private GradleBuildProfileSpan.Builder create(
            @NonNull ProfileRecordWriter profileRecordWriter,
            @NonNull ExecutionType executionType,
            @Nullable GradleTransformExecution transform) {
        long thisRecordId = profileRecordWriter.allocateRecordId();
    
        // am I a child ?
        @Nullable
        Long parentId = recordStacks.get().peek();
    
        long startTimeInMs = System.currentTimeMillis();
    
        final GradleBuildProfileSpan.Builder currentRecord =
                GradleBuildProfileSpan.newBuilder()
                        .setId(thisRecordId)
                        .setType(executionType)
                        .setStartTimeInMs(startTimeInMs);
    
        if (transform != null) {
            currentRecord.setTransform(transform);
        }
    
        if (parentId != null) {
            currentRecord.setParentId(parentId);
        }
    
        currentRecord.setThreadId(threadId.get());
        recordStacks.get().push(thisRecordId);
        return currentRecord;
    }
    

    create方法的逻辑就是创建了一个GradleBuildProfileSpan.Builder对象,并且将一些线程相关的变量设置进去,并将thisRecordId保存到一个双向队列中。

    然后再看3的逻辑:

    private void write(
            @NonNull ProfileRecordWriter profileRecordWriter,
            @NonNull GradleBuildProfileSpan.Builder currentRecord,
            @NonNull String projectPath,
            @Nullable String variant) {
        // pop this record from the stack.
        if (recordStacks.get().pop() != currentRecord.getId()) {
            Logger.getLogger(ThreadRecorder.class.getName())
                    .log(Level.SEVERE, "Profiler stack corrupted");
        }
        currentRecord.setDurationInMs(
                System.currentTimeMillis() - currentRecord.getStartTimeInMs());
        profileRecordWriter.writeRecord(projectPath, variant, currentRecord);
    }
    

    其中执行了ProfileRecordWriter对象的writeRecord方法:

    @Override
    public void writeRecord(
            @NonNull String project,
            @Nullable String variant,
            @NonNull final GradleBuildProfileSpan.Builder executionRecord) {
    
        executionRecord.setProject(mNameAnonymizer.anonymizeProjectPath(project));
        executionRecord.setVariant(mNameAnonymizer.anonymizeVariant(project, variant));
        spans.add(executionRecord.build());
    }
    

    最后运用建造者模式生成GradleBuildProfileSpan对象写入到ProfileRecordWriter对象的span(队列)变量中。

    最后我们看2的逻辑,即回调方法的执行。还记得上面提到过的BasePlugin的apply方法执行的三个record方法吗?其中每一个record方法都有自己的回调方法,即configureProject、configureExtension、createTasks。

    其实从这三个方法传进来的第一个参数,我们能大概看出每一个方法实现的逻辑:
    1、BASE_PLUGIN_PROJECT_CONFIGURE:插件的基本配置信息、初始化等。
    2、BASE_PLUGIN_PROJECT_BASE_EXTENSION_CREATION:插件Extension的初始化。
    3、BASE_PLUGIN_PROJECT_TASKS_CREATION:插件任务的创建。

    我们先来看第一个第一个方法的实现逻辑,后面两个方法放到后面的两篇文章来讲。

    private void configureProject() {
        final Gradle gradle = project.getGradle();
    
        extraModelInfo = new ExtraModelInfo(project.getPath(), projectOptions, project.getLogger());
        checkGradleVersion(project, getLogger(), projectOptions);
    
        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 =
                new AndroidBuilder(
                        project == project.getRootProject() ? project.getName() : project.getPath(),
                        creator,
                        new GradleProcessExecutor(project),
                        new GradleJavaProcessExecutor(project),
                        extraModelInfo.getSyncIssueHandler(),
                        extraModelInfo.getMessageReceiver(),
                        getLogger(),
                        isVerbose());
        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());
        }
    
        // Apply the Java plugin
        project.getPlugins().apply(JavaBasePlugin.class);
    
        project.getTasks()
                .getByName("assemble")
                .setDescription(
                        "Assembles all variants of all applications and secondary packages.");
    
        // call back on execution. This is called after the whole build is done (not
        // after the current project is done).
        // This is will be called for each (android) projects though, so this should support
        // being called 2+ times.
        gradle.addBuildListener(
                new BuildListener() {
                    @Override
                    public void buildStarted(@NonNull Gradle gradle) {
                        TaskInputHelper.enableBypass();
                        BuildableArtifactImpl.Companion.disableResolution();
                    }
    
                    @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;
                        }
                        sdkHandler.unload();
                        threadRecorder.record(
                                ExecutionType.BASE_PLUGIN_BUILD_FINISHED,
                                project.getPath(),
                                null,
                                () -> {
                                    WorkerActionServiceRegistry.INSTANCE
                                            .shutdownAllRegisteredServices(
                                                    ForkJoinPool.commonPool());
                                    PreDexCache.getCache()
                                            .clear(
                                                    FileUtils.join(
                                                            project.getRootProject().getBuildDir(),
                                                            FD_INTERMEDIATES,
                                                            "dex-cache",
                                                            "cache.xml"),
                                                    getLogger());
                                    Main.clearInternTables();
                                });
                    }
                });
    
        gradle.getTaskGraph()
                .addTaskExecutionGraphListener(
                        taskGraph -> {
                            TaskInputHelper.disableBypass();
                            Aapt2DaemonManagerService.registerAaptService(
                                    Objects.requireNonNull(androidBuilder.getTargetInfo())
                                            .getBuildTools(),
                                    loggerWrapper,
                                    WorkerActionServiceRegistry.INSTANCE);
    
                            for (Task task : taskGraph.getAllTasks()) {
                                if (task instanceof TransformTask) {
                                    Transform transform = ((TransformTask) task).getTransform();
                                    if (transform instanceof DexTransform) {
                                        PreDexCache.getCache()
                                                .load(
                                                        FileUtils.join(
                                                                project.getRootProject()
                                                                        .getBuildDir(),
                                                                FD_INTERMEDIATES,
                                                                "dex-cache",
                                                                "cache.xml"));
                                        break;
                                    }
                                }
                            }
                        });
    
        createLintClasspathConfiguration(project);
    }
    

    configureProject这个方法代码有点长,但是其主要执行了以下几个逻辑:
    1、初始化了SdkHandler、AndroidBuilder和DataBindingBuilder对象。
    2、依赖了JavaBasePlugin插件,在其中创建了很多关于构建的task,其中build、assemble等task就是在里面创建的。
    3、对项目的创建做了监听,在构建结束后执行了磁盘缓存等操作。

    四、总结

    本文主要介绍了如何查看Android Plugin插件源码,以及对Android Plugin插件的执行流程进行了简单的分析,后面会继续接着分析插件Extension和插件任务的创建这两个流程。

    相关文章

      网友评论

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

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