Android开发

Android Gradle Plugin —— 初窥门径 (三)

本文主要是介绍Android Gradle Plugin —— 初窥门径 (三),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

在如今这个时代,仅仅会简单的页面开发已经很难找到好工作了。

所以在实习的业余时间,我开始学习Android的Gradle构建流程,并且想将此心路历程整理为博客,方便加深理解。

简述

在本文,我将给大家介绍几点:

  1. IncrementalTask 是如何实现增量的?—— IncrementalTask

  2. AndroidManifest是如何被合并的?—— ProcessManifest

  3. Resources如何被处理的?—— MergeResources

  4. BuildConfig是如何生成的?—— GenerateBuildConfig

PS:本文基于Gradle 3.2.1版本

ApplicationTaskManager

​ 首先,我们在上一篇文章中讲解了,在AbstractAppPlugin中初始化了了ApplicationTaskManager。而ApplicationTaskManager中创建了许许多多的Task,包含了整个构建流程。从下文中我们可以简单地查看有哪些Task。

@Override
    public void createTasksForVariantScope(@NonNull final VariantScope variantScope) {
        BaseVariantData variantData = variantScope.getVariantData();

        createAnchorTasks(variantScope);
        createCheckManifestTask(variantScope);

        handleMicroApp(variantScope);

        // Create all current streams (dependencies mostly at this point)
        createDependencyStreams(variantScope);

        // Add a task to publish the applicationId.
        createApplicationIdWriterTask(variantScope);

        taskFactory.create(new MainApkListPersistence.ConfigAction(variantScope));
        createBuildArtifactReportTask(variantScope);

        // Add a task to process the manifest(s)
        createMergeApkManifestsTask(variantScope);

        // Add a task to create the res values
        createGenerateResValuesTask(variantScope);

        // Add a task to compile renderscript files.
        createRenderscriptTask(variantScope);

        // Add a task to merge the resource folders
        createMergeResourcesTask(
                variantScope,
                true,
                Sets.immutableEnumSet(MergeResources.Flag.PROCESS_VECTOR_DRAWABLES));

        // Add tasks to compile shader
        createShaderTask(variantScope);

        // Add a task to merge the asset folders
        createMergeAssetsTask(variantScope);

        // Add a task to create the BuildConfig class
        createBuildConfigTask(variantScope);

        // Add a task to process the Android Resources and generate source files
        createApkProcessResTask(variantScope);

        // Add a task to process the java resources
        createProcessJavaResTask(variantScope);

        createAidlTask(variantScope);

        // Add NDK tasks
        createNdkTasks(variantScope);
        variantScope.setNdkBuildable(getNdkBuildable(variantData));

        // Add external native build tasks
        createExternalNativeBuildJsonGenerators(variantScope);
        createExternalNativeBuildTasks(variantScope);

        // Add a task to merge the jni libs folders
        createMergeJniLibFoldersTasks(variantScope);

        // Add feature related tasks if necessary
        if (variantScope.getType().isBaseModule()) {
            // Base feature specific tasks.
            taskFactory.create(new FeatureSetMetadataWriterTask.ConfigAction(variantScope));

            if (extension.getDataBinding().isEnabled()) {
                // Create a task that will package the manifest ids(the R file packages) of all
                // features into a file. This file's path is passed into the Data Binding annotation
                // processor which uses it to known about all available features.
                //
                // <p>see: {@link TaskManager#setDataBindingAnnotationProcessorParams(VariantScope)}
                taskFactory.create(
                        new DataBindingExportFeatureApplicationIdsTask.ConfigAction(variantScope));

            }
        } else {
            // Non-base feature specific task.
            // Task will produce artifacts consumed by the base feature
            taskFactory.create(new FeatureSplitDeclarationWriterTask.ConfigAction(variantScope));
            if (extension.getDataBinding().isEnabled()) {
                // Create a task that will package necessary information about the feature into a
                // file which is passed into the Data Binding annotation processor.
                taskFactory.create(new DataBindingExportFeatureInfoTask.ConfigAction(variantScope));
            }
            taskFactory.create(new MergeConsumerProguardFilesConfigAction(variantScope));
        }

        // Add data binding tasks if enabled
        createDataBindingTasksIfNecessary(variantScope, MergeType.MERGE);

        // Add a compile task
        createCompileTask(variantScope);

        if (variantScope.getType().isBaseModule()) {
            CheckMultiApkLibrariesTask checkMultiApkLibrariesTask =
                    taskFactory.create(new CheckMultiApkLibrariesTask.ConfigAction(variantScope));
            // variantScope.setMergeJavaResourcesTask() is called in createCompileTask() above.
            // We set the merge java resources task to depend on this check, because merging java
            // resources is the first place an error could be thrown if there are duplicate
            // libraries.
            variantScope
                    .getTaskContainer()
                    .getMergeJavaResourcesTask()
                    .dependsOn(checkMultiApkLibrariesTask);
        }

        createStripNativeLibraryTask(taskFactory, variantScope);


        if (variantScope.getVariantData().getMultiOutputPolicy().equals(MultiOutputPolicy.SPLITS)) {
            if (extension.getBuildToolsRevision().getMajor() < 21) {
                throw new RuntimeException(
                        "Pure splits can only be used with buildtools 21 and later");
            }

            createSplitTasks(variantScope);
        }


        BuildInfoWriterTask buildInfoWriterTask = createInstantRunPackagingTasks(variantScope);
        createPackagingTask(variantScope, buildInfoWriterTask);

        // Create the lint tasks, if enabled
        createLintTasks(variantScope);

        taskFactory.create(new FeatureSplitTransitiveDepsWriterTask.ConfigAction(variantScope));

        createDynamicBundleTask(variantScope);
    }
复制代码

IncrementalTask

​ 上述大部分的Task继承于IncrementalTask。所以说,如果想分析部分的Task,首先需要了解IncrementalTask的主要逻辑。了解了这个Task的逻辑之后,你就能知道代码应该从哪里看起了。

public abstract class IncrementalTask extends AndroidBuilderTask {

    public static final String MARKER_NAME = "build_was_incremental";

    private File incrementalFolder;

    public void setIncrementalFolder(File incrementalFolder) {
        this.incrementalFolder = incrementalFolder;
    }

    @OutputDirectory @Optional
    public File getIncrementalFolder() {
        return incrementalFolder;
    }

    //是否增量
    @Internal
    protected boolean isIncremental() {
        return false;
    }
  
  	//全量构建的时候走这个方法
    protected abstract void doFullTaskAction() throws Exception;

  	//增量构建的时候,走这个方法
    protected void doIncrementalTaskAction(Map<File, FileStatus> changedInputs) throws Exception {
        // do nothing.
    }

  	//task的入口
    @TaskAction
    void taskAction(IncrementalTaskInputs inputs) throws Exception {
      	//如果不是增量Task,或者输入不是增量,则走全量构建的方法 —— doFullTaskAction
        if (!isIncremental() || !inputs.isIncremental()) {
            getProject().getLogger().info("Unable do incremental execution: full task run");
            doFullTaskAction();
            return;
        }
				// 否则,走增量方法
        doIncrementalTaskAction(getChangedInputs(inputs));
    }
	
  	//增量构建时,获取改变的inputs
    private Map<File, FileStatus> getChangedInputs(IncrementalTaskInputs inputs) {
        final Map<File, FileStatus> changedInputs = Maps.newHashMap();
				
      	//遍历过期的inputs,将其加入map
        inputs.outOfDate(
                change -> {
                    FileStatus status = change.isAdded() ? FileStatus.NEW : FileStatus.CHANGED;
                    changedInputs.put(change.getFile(), status);
                });
				
      	//遍历被移除的inputs,将其加入map
        inputs.removed(change -> changedInputs.put(change.getFile(), FileStatus.REMOVED));

        return changedInputs;
    }
}
复制代码

​ 上面的逻辑很简单,总结一下就是:

1. 走全量构建时,走doFullTaskAction方法

2. 走增量构建时,走doIncrementalTaskAction,同时计算change的inputs。
复制代码

​ 了解了IncrementalTask,我们就可以分析一下其他简单的Task了。

ProcessManifest

​ 这个Task用于合并各个AndroidManifest,最终继承于IncrementalTask,而这个方法并没有覆写isIncremental方法,所以说是不支持增量的,于是我们只需要看doFullTaskAction方法。另外,AGP中,许许多多的Task中的成员变量,基本都是利用TaskConfigAction(一般都是Task中的静态内部类)进行配置的,这个Task也不例外。

 public static class ConfigAction implements TaskConfigAction<MergeManifests>
复制代码

​ 好了,我们看看doFullTaskAction方法

ProcessManifest.doFullTaskAction

    @Override
    protected void doFullTaskAction() throws IOException {
        // read the output of the compatible screen manifest.
        BuildElements compatibleScreenManifests =
                ExistingBuildElements.from(
                        InternalArtifactType.COMPATIBLE_SCREEN_MANIFEST, compatibleScreensManifest);

        
      	//省略了一些check

        @Nullable BuildOutput compatibleScreenManifestForSplit;

        ImmutableList.Builder<BuildOutput> mergedManifestOutputs = ImmutableList.builder();
				//...省略了InstantRun逻辑
      
        // FIX ME : multi threading.
        // TODO : LOAD the APK_LIST FILE .....
      	//这里遍历不同variant产生个Apk集合
        for (ApkData apkData : outputScope.getApkDatas()) {

            compatibleScreenManifestForSplit = compatibleScreenManifests.element(apkData);
            File manifestOutputFile =
                    FileUtils.join(
                            getManifestOutputDirectory(),
                            apkData.getDirName(),
                            SdkConstants.ANDROID_MANIFEST_XML);
            //...省略了InstantRun逻辑
          
          	//MergingReport包含了Manifest合并的结果
            MergingReport mergingReport =
                    getBuilder()
              							//这里包含了主要的Merge逻辑
                            .mergeManifestsForApplication(
                                    getMainManifest(),
                                    getManifestOverlays(),
                                    computeFullProviderList(compatibleScreenManifestForSplit),
                                    getNavigationFiles(),
                                    getFeatureName(),
                                    moduleMetadata == null
                                            ? getPackageOverride()
                                            : moduleMetadata.getApplicationId(),
                                    moduleMetadata == null
                                            ? apkData.getVersionCode()
                                            : Integer.parseInt(moduleMetadata.getVersionCode()),
                                    moduleMetadata == null
                                            ? apkData.getVersionName()
                                            : moduleMetadata.getVersionName(),
                                    getMinSdkVersion(),
                                    getTargetSdkVersion(),
                                    getMaxSdkVersion(),
                                    manifestOutputFile.getAbsolutePath(),
                                    // no aapt friendly merged manifest file necessary for applications.
                                    null /* aaptFriendlyManifestOutputFile */,
                                    instantRunManifestOutputFile.getAbsolutePath(),
                                    ManifestMerger2.MergeType.APPLICATION,
                                    variantConfiguration.getManifestPlaceholders(),
                                    getOptionalFeatures(),
                                    getReportFile());
						//获取MergingReport中MERGE种类的XmlDocument
            XmlDocument mergedXmlDocument =
                    mergingReport.getMergedXmlDocument(MergingReport.MergedManifestKind.MERGED);
						//从xml document中提取出各个属性
            ImmutableMap<String, String> properties =
                    mergedXmlDocument != null
                            ? ImmutableMap.of(
                                    "packageId",
                                    mergedXmlDocument.getPackageName(),
                                    "split",
                                    mergedXmlDocument.getSplitName(),
                                    SdkConstants.ATTR_MIN_SDK_VERSION,
                                    mergedXmlDocument.getMinSdkVersion())
                            : ImmutableMap.of();
						//将属性添加到输出里
            mergedManifestOutputs.add(
                    new BuildOutput(
                            InternalArtifactType.MERGED_MANIFESTS,
                            apkData,
                            manifestOutputFile,
                            properties));
            //...省略了InstantRun逻辑
        }
        new BuildElements(mergedManifestOutputs.build()).save(getManifestOutputDirectory());
        //...省略了InstantRun逻辑
    }
复制代码

​ 根据我的注释,我们可以分为以下几步:

1. 首先会遍历ApkDatas,每一个variant对应一个ApkData

2. 进行Manifest的Merge逻辑,将结果保存在MergingReport中
3. 在MergingReport中,在其一个map中获取属性为MERGED的XmlDocument
4. 从XmlDocument中提取各个属性,然后作为一个BuildOutput对象存放在一个集合中
5. 将这个集合构建为BuildElement,save方法是将对象序列化为一个Json文件保存到Manifest的输出文件夹中。
复制代码

​ 在这几步中,我最想了解的就是Manifest的是如何merge的,于是我们需要追踪mergeManifestsForApplication方法。

AndroidBuilder.mergeManifestsForApplication

   public MergingReport mergeManifestsForApplication(
            ....) {

        try {
						//Invoker主要是用于存放Manifest合并所需要的所有数据,在其构造方法中会检查必要的MainManifest。
            Invoker manifestMergerInvoker =
                    ManifestMerger2.newMerger(mainManifest, mLogger, mergeType)
                            .setPlaceHolderValues(placeHolders)
                            .addFlavorAndBuildTypeManifests(
                                    manifestOverlays.toArray(new File[manifestOverlays.size()]))
                            .addManifestProviders(dependencies)
                            .addNavigationFiles(navigationFiles)
                            .withFeatures(
                                    optionalFeatures.toArray(
                                            new Invoker.Feature[optionalFeatures.size()]))
                            .setMergeReportFile(reportFile)
                            .setFeatureName(featureName);

            //...
						//这些属性可以直接覆盖ManifestSystemProperty中对应的属性
            setInjectableValues(manifestMergerInvoker,
                    packageOverride, versionCode, versionName,
                    minSdkVersion, targetSdkVersion, maxSdkVersion);
						//调用merge方法,这里就开始merge了,很关键~~!!
            MergingReport mergingReport = manifestMergerInvoker.merge();
            mLogger.verbose("Merging result: %1$s", mergingReport.getResult());
          	//根据merge的结果进行不同的处理,例如成功就直接写入文件了
            switch (mergingReport.getResult()) {
                case WARNING:
                   //...
                case SUCCESS:
                    //....
                    break;
                case ERROR:
                    //...
                default:
                    //...
            }
            return mergingReport;
        } catch (ManifestMerger2.MergeFailureException e) {
            // TODO: unacceptable.
            throw new RuntimeException(e);
        }
    }
复制代码

​ 逻辑也不是很复杂,创建一个Invoker保存所有Merge要用到的数据,之后再调用Merge方法进行合并。

Invoker.merge

        public MergingReport merge() throws MergeFailureException {

            // provide some free placeholders values.
            ImmutableMap<ManifestSystemProperty, Object> systemProperties = mSystemProperties.build();
            if (systemProperties.containsKey(ManifestSystemProperty.PACKAGE)) {
                // if the package is provided, make it available for placeholder replacement.
                mPlaceholders.put(PACKAGE_NAME, systemProperties.get(ManifestSystemProperty.PACKAGE));
                // as well as applicationId since package system property overrides everything
                // but not when output is a library since only the final (application)
                // application Id should be used to replace libraries "applicationId" placeholders.
                if (mMergeType != MergeType.LIBRARY) {
                    mPlaceholders.put(APPLICATION_ID, systemProperties.get(ManifestSystemProperty.PACKAGE));
                }
            }

            FileStreamProvider fileStreamProvider = mFileStreamProvider != null
                    ? mFileStreamProvider : new FileStreamProvider();
            ManifestMerger2 manifestMerger =
                    new ManifestMerger2(
                            mLogger,
                            mMainManifestFile,
                            mLibraryFilesBuilder.build(),
                            mFlavorsAndBuildTypeFiles.build(),
                            mFeaturesBuilder.build(),
                            mPlaceholders.build(),
                            new MapBasedKeyBasedValueResolver<ManifestSystemProperty>(
                                    systemProperties),
                            mMergeType,
                            mDocumentType,
                            Optional.fromNullable(mReportFile),
                            mFeatureName,
                            fileStreamProvider,
                            mNavigationFilesBuilder.build());
            return manifestMerger.merge();
        }
复制代码

​ 设置一些placeholder(占位符),之后用于替换,然后生成ManifestMerger2最终进行merge。

ManifestMerger2.merge

merge里面的代码较多,我们将其分为几个部分:

  1. 加载Main Manifest
// load the main manifest file to do some checking along the way.
        LoadedManifestInfo loadedMainManifestInfo = load(
                new ManifestInfo(
                        mManifestFile.getName(),
                        mManifestFile,
                        mDocumentType,
                        Optional.<String>absent() /* mainManifestPackageName */),
                selectors,
                mergingReportBuilder);
复制代码
  1. 进行一些检查,例如是否设置了package attribute
  2. 加载library的xml
// load all the libraries xml files early to have a list of all possible node:selector
        // values.
        List<LoadedManifestInfo> loadedLibraryDocuments =
                loadLibraries(
                        selectors,
                        mergingReportBuilder,
                        mainPackageAttribute.isPresent()
                                ? mainPackageAttribute.get().getValue()
                                : null);
复制代码
  1. 执行Manifest系统属性注入:例如version、version_name之类的
// perform system property injection
        performSystemPropertiesInjection(mergingReportBuilder,
                loadedMainManifestInfo.getXmlDocument());
复制代码
  1. 遍历mFlavorsAndBuildTypeFiles,进行一些check
Optional<XmlDocument> xmlDocumentOptional = Optional.absent();
        for (File inputFile : mFlavorsAndBuildTypeFiles) {
            mLogger.verbose("Merging flavors and build manifest %s \n", inputFile.getPath());
            LoadedManifestInfo overlayDocument = load(
                    new ManifestInfo(null, inputFile, XmlDocument.Type.OVERLAY,
                            Optional.of(mainPackageAttribute.get().getValue())),
                    selectors,
                    mergingReportBuilder);

            // 检查是否定义了package
            Optional<XmlAttribute> packageAttribute =
                    overlayDocument.getXmlDocument().getPackage();
            // 如果两个文件都定义了package,他们的package应该相同,不同则出错
            if (loadedMainManifestInfo.getOriginalPackageName().isPresent() &&
                    packageAttribute.isPresent()
                    && !loadedMainManifestInfo.getOriginalPackageName().get().equals(
                    packageAttribute.get().getValue())) {
                
                String message = mMergeType == MergeType.APPLICATION
                        ? String.format(
                                "Overlay manifest:package attribute declared at %1$s value=(%2$s)\n"
                                        + "\thas a different value=(%3$s) "
                                        + "declared in main manifest at %4$s\n"
                                        + "\tSuggestion: remove the overlay declaration at %5$s "
                                        + "\tand place it in the build.gradle:\n"
                                        + "\t\tflavorName {\n"
                                        + "\t\t\tapplicationId = \"%2$s\"\n"
                                        + "\t\t}",
                                packageAttribute.get().printPosition(),
                                packageAttribute.get().getValue(),
                                mainPackageAttribute.get().getValue(),
                                mainPackageAttribute.get().printPosition(),
                                packageAttribute.get().getSourceFile().print(true))
                        : String.format(
                                "Overlay manifest:package attribute declared at %1$s value=(%2$s)\n"
                                        + "\thas a different value=(%3$s) "
                                        + "declared in main manifest at %4$s",
                                packageAttribute.get().printPosition(),
                                packageAttribute.get().getValue(),
                                mainPackageAttribute.get().getValue(),
                                mainPackageAttribute.get().printPosition());
                mergingReportBuilder.addMessage(
                        overlayDocument.getXmlDocument().getSourceFile(),
                        MergingReport.Record.Severity.ERROR,
                        message);
                return mergingReportBuilder.build();
            }
           //...
        }
复制代码
  1. merge library的时候,强制将mainManifest的package注入进去
if (mMergeType == MergeType.LIBRARY) {
            // extract the package name...
            String mainManifestPackageName = loadedMainManifestInfo.getXmlDocument().getRootNode()
                    .getXml().getAttribute("package");
            // save it in the selector instance.
            if (!Strings.isNullOrEmpty(mainManifestPackageName)) {
                xmlDocumentOptional.get().getRootNode().getXml()
                        .setAttribute("package", mainManifestPackageName);
            }
        }
复制代码
  1. Merge library的Manifest
for (LoadedManifestInfo libraryDocument : loadedLibraryDocuments) {
            mLogger.verbose("Merging library manifest " + libraryDocument.getLocation());
            xmlDocumentOptional = merge(
                    xmlDocumentOptional, libraryDocument, mergingReportBuilder);
            if (!xmlDocumentOptional.isPresent()) {
                return mergingReportBuilder.build();
            }
        }
复制代码
  1. 替换Manifest中的PlaceHolder
if (!mOptionalFeatures.contains(Invoker.Feature.NO_PLACEHOLDER_REPLACEMENT)) {
            // do one last placeholder substitution, this is useful as we don't stop the build
            // when a library failed a placeholder substitution, but the element might have
            // been overridden so the problem was transient. However, with the final document
            // ready, all placeholders values must have been provided.
            MergingReport.Record.Severity severity =
                    mMergeType == MergeType.LIBRARY
                            ? MergingReport.Record.Severity.INFO
                            : MergingReport.Record.Severity.ERROR;
            performPlaceHolderSubstitution(
                    loadedMainManifestInfo,
                    xmlDocumentOptional.get(),
                    mergingReportBuilder,
                    severity);
            if (mergingReportBuilder.hasErrors()) {
                return mergingReportBuilder.build();
            }
        }
复制代码
  1. 对最终的Manifest(finalMergedDocument)再进行一些处理,这里我们就不关心了
  2. 最终生成合并后的AndroidManifest.xml文件

至此,一个完整的Manifest合并流程就结束了。

MergeResources

​ 查看MergeResources这个Task,我们发现继承于IncrementalTask,同时该Task覆写了isIncremental,返回true。

​ 所以说,我们需要查看doFullTaskAction方法以及doIncrementalTaskAction方法。

MergeResources.doFullTaskAction

这个方法步骤很多,我们查看主要步骤就可以了。

  1. 清理之前的输出文件夹
				File destinationDir = getOutputDir();
        FileUtils.cleanOutputDir(destinationDir);
        if (dataBindingLayoutInfoOutFolder != null) {
            FileUtils.deleteDirectoryContents(dataBindingLayoutInfoOutFolder);
        }
复制代码
  1. 获取资源文件
List<ResourceSet> resourceSets = getConfiguredResourceSets(preprocessor);
复制代码

这个方法会返回一个ResourceSet集合,里面主要是自己的res、library的res以及generated的res

  1. 创建ResourceMerger,并且用刚刚获取的ResourceSet集合进行填充
			// create a new merger and populate it with the sets.
        ResourceMerger merger = new ResourceMerger(minSdk.get());
        MergingLog mergingLog = null;
        if (blameLogFolder != null) {
            FileUtils.cleanOutputDir(blameLogFolder);
            mergingLog = new MergingLog(blameLogFolder);
        }
				
				//ResourceCompilationService根据aaptGeneration判断,使用AAPT或者AAPT2等
        try (ResourceCompilationService resourceCompiler =
                getResourceProcessor(....)) {

            for (ResourceSet resourceSet : resourceSets) {
                resourceSet.loadFromFiles(getILogger());
              	//填充merger
                merger.addDataSet(resourceSet);
            }
复制代码
  1. 创建MergedResourceWriter,这个类负责编译资源文件以及从layout文件中剥离Databinding的东西
MergedResourceWriter writer =
                    new MergedResourceWriter(
                            workerExecutorFacade,
                            destinationDir,
                            getPublicFile(),
                            mergingLog,
                            preprocessor,
                            resourceCompiler,
                            getIncrementalFolder(),
                            dataBindingLayoutProcessor,
                            mergedNotCompiledResourcesOutputDirectory,
                            pseudoLocalesEnabled,
                            getCrunchPng());
复制代码
  1. 合并资源
merger.mergeData(writer, false /*doCleanUp*/);
复制代码
  1. 将MergedResourceWriter传入该方法后,调用MergedResourceWriter的start、ignoreItemInMerge、removeItem、addItem和end方法对资源进行处理,上述方法中的Item代表这一个资源文件(res目录下的每个文件,可能是图片资源,也可能是xml文件)。每一个Item会在MergedResourceWriter中转化为一个CompileResourceRequest,然后添加到mCompileResourceRequests这个集合中,该集合定义如下:
@NonNull
    private final ConcurrentLinkedQueue<CompileResourceRequest> mCompileResourceRequests =
            new ConcurrentLinkedQueue<>();
复制代码

​ 那么问题来了,为什么会使用Concurrent包中的队列来保证并发性呢?—— 在removeItem中有这样一段注释

/*
* There are two reasons to skip this: 1. we save an IO operation by
* deleting a file that will be overwritten. 2. if we did delete the file,
* we would have to be careful about concurrency to make sure we would be
* deleting the *old* file and not the overwritten version.
*/
复制代码

​ 我们要确保删除的文件的文件是老版本的文件,而不是覆盖的新版本的文件。

  1. 在MergedResourceWriter的start方法中,初始化了一些变量。而在end方法中,则是处理mCompileResourceRequests了。关键地方如下:
					while (!mCompileResourceRequests.isEmpty()) {
                CompileResourceRequest request = mCompileResourceRequests.poll();
                try {
                    //...处理DataBinding相关逻辑
	
                  	//mResourceCompiler是个接口,实现其接口的类为Aapt2相关的类
                  	//调用submitCompile方法,利用Aapt2命令编译资源文件
                    mResourceCompiler.submitCompile(
                            new CompileResourceRequest(
                                    fileToCompile,
                                    request.getOutputDirectory(),
                                    request.getInputDirectoryName(),
                                    pseudoLocalesEnabled,
                                    crunchPng,
                                    ImmutableMap.of(),
                                    request.getInputFile()));
                  	//编译完成之后就将文件放入这个map中
                    mCompiledFileMap.put(
                            fileToCompile.getAbsolutePath(),
                            mResourceCompiler.compileOutputFor(request).getAbsolutePath());

                } catch (ResourceCompilationException | IOException e) {
                    throw MergingException.wrapException(e)
                            .withFile(request.getInputFile())
                            .build();
                }
            }

						//...后面释放一些资源,然后调用mCompiledFileMap.store方法将map存储下来
复制代码

​ 至此,MergeResources的过程就结束了。

GenerateBuildConfig

​ 我们经常使用BuildConfig.java这个类,来获取一些Gradle的配置属性,那么你一定很好奇BuildConfig这个类是如何生成的吧?

​ GenerateBuildConfig这个类就是一个专门用于生成BuildConfig的Task。

​ 首先,我们查看@TaskAction注解标注的方法,这是Task的入口。

		@TaskAction
    void generate() throws IOException {
      	//在packageName改变的时候,一定要删除输出文件夹,否则会有两个类
        File destinationDir = getSourceOutputDir();
        FileUtils.cleanOutputDir(destinationDir);
			
      	//创建BuildConfigGenerator,配置PackageName和输出文件夹
        BuildConfigGenerator generator = new BuildConfigGenerator(
                getSourceOutputDir(),
                getBuildConfigPackageName());

        // 利用Generator添加BuildConfig里面的Field
        generator
                .addField(
                        "boolean",
                        "DEBUG",
                        isDebuggable() ? "Boolean.parseBoolean(\"true\")" : "false")
                .addField("String", "APPLICATION_ID", '"' + appPackageName.get() + '"')
                .addField("String", "BUILD_TYPE", '"' + getBuildTypeName() + '"')
                .addField("String", "FLAVOR", '"' + getFlavorName() + '"')
                .addField("int", "VERSION_CODE", Integer.toString(getVersionCode()))
                .addField(
                        "String", "VERSION_NAME", '"' + Strings.nullToEmpty(getVersionName()) + '"')
          			//上面那些都是基本的属性,getItems()里面添加的就是自定义属性
                .addItems(getItems());

        List<String> flavors = getFlavorNamesWithDimensionNames();
        int count = flavors.size();
        if (count > 1) {
            for (int i = 0; i < count; i += 2) {
              	//添加flavor
                generator.addField(
                        "String", "FLAVOR_" + flavors.get(i + 1), '"' + flavors.get(i) + '"');
            }
        }
				//最后生成一个类
        generator.generate();
    }
复制代码

​ 逻辑很简单,接下来我们看看getItems()里面会获取到哪些属性。这个items最初是由VariantConfiguration.getBuildConfigItems赋值的:

@NonNull
public List<Object> getBuildConfigItems() {
    List<Object> fullList = Lists.newArrayList();

    // keep track of the names already added. This is because we show where the items
    // come from so we cannot just put everything a map and let the new ones override the
    // old ones.
    Set<String> usedFieldNames = Sets.newHashSet();

  	//添加Variant特定的Field
    Collection<ClassField> list = mBuildConfigFields.values();
    if (!list.isEmpty()) {
        fullList.add("Fields from the variant");
        fillFieldList(fullList, usedFieldNames, list);
    }

  	//添加Key为Filed的属性
    list = mBuildType.getBuildConfigFields().values();
    if (!list.isEmpty()) {
        fullList.add("Fields from build type: " + mBuildType.getName());
        fillFieldList(fullList, usedFieldNames, list);
    }
		
  	//添加每个Flavor中key为Field的属性
    for (F flavor : mFlavors) {
        list = flavor.getBuildConfigFields().values();
        if (!list.isEmpty()) {
            fullList.add("Fields from product flavor: " + flavor.getName());
            fillFieldList(fullList, usedFieldNames, list);
        }
    }
		
  	//添加默认Config中key为Field的属性
    list = mDefaultConfig.getBuildConfigFields().values();
    if (!list.isEmpty()) {
        fullList.add("Fields from default config.");
        fillFieldList(fullList, usedFieldNames, list);
    }

    return fullList;
}
复制代码

所以说我们在Gradle中定义BuildConfigField,就是这么生成的。

buildConfigField("String","HAHA","\"haahahah\"")
复制代码

之后再将其属性写入BuildConfig.java中就可以了。

后续

​ 本文为大家讲解了如何去分析各种AGP的Task,如果大家想查看其他的AGP Task源码,则需要自己去分析啦。如果有问题,可以直接在评论中提出哈。

​ 最后吐槽:AGP源码很容易大改,着实让学习者摸不清头脑。

这篇关于Android Gradle Plugin —— 初窥门径 (三)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!