Android gradle打包涉及task源码解析(三)

文章序号

  • Android gradle打包涉及task源码解析(一)准备工作
  • Android gradle打包涉及task源码解析(二)
  • Android gradle打包涉及task源码解析(三)
  • Android gradle打包涉及task源码解析(四)
  • Android gradle打包涉及task源码解析(五)
  • Android gradle打包涉及task源码解析(六)

此篇文章将分析如下5个task。

:app:generateDebugResValues UP-TO-DATE
:app:generateDebugResources UP-TO-DATE
:app:mergeDebugResources UP-TO-DATE
:app:createDebugCompatibleScreenManifests UP-TO-DATE
:app:processDebugManifest

generateDebugResValues

  • 准备

在项目的app/gradle中添加如下代码

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            // 添加代码
            resValue "string", "AppName", "app_release"
        }
        // 添加代码
        debug {
            resValue "string", "AppName", "app_debug"
        }
    }

接着执行命令./gradlew generateDebugResValues

  • inputs&outputs
output file:/Users/zhengchao/Documents/AndroidSpace/OpenSpace/TasksPro/app/build/generated/res/resValues/debug

输出文件目录下面会生成generated.xml文件,文件内容如下:




    

    
    app_debug


生成的内容我们可以直接在xml中使用(android:text="@string/AppName"),也可以在代码中使用(getResources().getString(R.string.AppName);)。

  • 源码

https://android.googlesource.com/platform/tools/base/+/gradle_3.0.0/build-system/gradle-core/src/main/java/com/android/build/gradle/tasks/GenerateResValues.java

  • 主要代码逻辑

GenerateResValues.java中的generate()方法

    @TaskAction
    void generate() throws IOException, ParserConfigurationException {
        // 1、输出路径,就是我们output中的目录
        File folder = getResOutputDir();
        // 2、这里的getItems()就是获得我们在代码中设置的resValues
        List resolvedItems = getItems();

        if (resolvedItems.isEmpty()) {
            FileUtils.cleanOutputDir(folder);
        } else {
            // 3、在resolvedItems不等于空的时候,通过ResValueGenerator生成我们的generated.xml文件
            ResValueGenerator generator = new ResValueGenerator(folder);
            generator.addItems(getItems());
            generator.generate();
        }
    }
 
 

通过上面的分析,我们知道generateDebugResValues 任务就是把我们在gradle里面配置的resValue读取到,然后在/build/generated/res/resValues/debug目录下生成generate.xml文件,该文件里面的内容,可直接在xml文件和代码中使用,方便一些动态化的配置。

generateDebugResources

很遗憾该任务的相关逻辑没有找到,有知道的麻烦留言告知一下。

mergeDebugResources

执行命令./gradlew mergeDebugResources

  • inputs&outputs
input file:/Users/zhengchao/Documents/AndroidSpace/OpenSpace/TasksPro/app/build/generated/res/resValues/debug
input file:/Users/zhengchao/.gradle/caches/transforms-1/files-1.1/appcompat-v7-26.1.0.aar/a7cc521b4567369eba0ddb355f44a660/res
input file:/Users/zhengchao/.gradle/caches/transforms-1/files-1.1/constraint-layout-1.1.3.aar/604f3f8b356720eda7bfab425c06a06e/res
input file:/Users/zhengchao/.gradle/caches/transforms-1/files-1.1/support-media-compat-26.1.0.aar/500188dffd88c5be8587eb6372bbf06d/res
input file:/Users/zhengchao/.gradle/caches/transforms-1/files-1.1/support-compat-26.1.0.aar/9806df9e60c4aacc7f9f357a91ad2e92/res
input file:/Users/zhengchao/Documents/AndroidSpace/OpenSpace/TasksPro/app/build/generated/res/rs/debug
input file:/Users/zhengchao/Documents/AndroidSpace/OpenSpace/TasksPro/app/src/main/res
input file:/Users/zhengchao/Documents/AndroidSpace/OpenSpace/TasksPro/app/src/debug/res
---------------------------------------------------
output file:/Users/zhengchao/Documents/AndroidSpace/OpenSpace/TasksPro/app/build/intermediates/blame/res/debug
output file:/Users/zhengchao/Documents/AndroidSpace/OpenSpace/TasksPro/app/build/generated/res/pngs/debug
output file:/Users/zhengchao/Documents/AndroidSpace/OpenSpace/TasksPro/app/build/intermediates/incremental/mergeDebugResources
output file:/Users/zhengchao/Documents/AndroidSpace/OpenSpace/TasksPro/app/build/intermediates/res/merged/debug

根据输入文件路径,大致有这几种:

1、generateDebugResValues任务生成的resValues文件(generated/res/resValues/debug);

2、引用的aar包里面的资源(appcompat-v7-26.1.0.aar/a7cc521b4567369eba0ddb355f44a660/res,constraint-layout-1.1.3.aar/604f3f8b356720eda7bfab425c06a06e/res);

3、compileDebugRenderscript 任务生的Renderscript文件(generated/res/rs/debug);

4、项目中的res资源文件(TasksPro/app/src/main/resTasksPro/app/src/debug/res);

输入文件经过mergeDebugResources任务处理后,生产的文件有如下几种:

1、png图片集合(generated/res/pngs/debug);

2、merge后的资源集合(incremental/mergeDebugResources);

3、资源映射关系集合(intermediates/res/merged/debug);

4、merge操作的日志记录(intermediates/blame/res/debug);

  • 源码

https://android.googlesource.com/platform/tools/base/+/gradle_3.0.0/build-system/gradle-core/src/main/java/com/android/build/gradle/tasks/MergeResources.java

  • 主要代码逻辑

MergeResources.java->doFullTaskAction()方法

    @Override
    protected void doFullTaskAction() throws IOException, ExecutionException, JAXBException {
        // 1、得到ResourcePreprocessor子类对象
        ResourcePreprocessor preprocessor = getPreprocessor();

        // 2、得到task的output目录
        // this is full run, clean the previous output
        File destinationDir = getOutputDir();
        FileUtils.cleanOutputDir(destinationDir);

        // 3、得到inputs文件目录集合
        List resourceSets = getConfiguredResourceSets(preprocessor);

        // 4、生成ResourceMerger 对象
        // create a new merger and populate it with the sets.
        ResourceMerger merger = new ResourceMerger(minSdk);
        // 5、mergeingLog 记录
        MergingLog mergingLog =
                getBlameLogFolder() != null ? new MergingLog(getBlameLogFolder()) : null;

        // 6、resourceCompiler,实际是Appt工具对象
        try (QueueableResourceCompiler resourceCompiler =
                processResources
                        ? makeAapt(
                                aaptGeneration,
                                getBuilder(),
                                fileCache,
                                crunchPng,
                                variantScope,
                                getAaptTempDir(),
                                mergingLog)
                        : QueueableResourceCompiler.NONE) {

            for (ResourceSet resourceSet : resourceSets) {
                resourceSet.loadFromFiles(getILogger());
                merger.addDataSet(resourceSet);
            }

            MergedResourceWriter writer =
                    new MergedResourceWriter(
                            workerExecutorFacade,
                            destinationDir,
                            getPublicFile(),
                            mergingLog,
                            preprocessor,
                            resourceCompiler,
                            getIncrementalFolder(),
                            dataBindingLayoutProcessor,
                            mergedNotCompiledResourcesOutputDirectory,
                            pseudoLocalesEnabled,
                            getCrunchPng());
            // 7、执行merge resource 操作
            merger.mergeData(writer, false /*doCleanUp*/);

            if (dataBindingLayoutProcessor != null) {
                dataBindingLayoutProcessor.end();
            }

            // No exception? Write the known state.
            merger.writeBlobTo(getIncrementalFolder(), writer, false);
        } catch (MergingException e) {
            System.out.println(e.getMessage());
            merger.cleanBlob(getIncrementalFolder());
            throw new ResourceException(e.getMessage(), e);
        } finally {
            cleanup();
        }
    }

第一步:得到ResourcePreprocessor子类对象,实际是MergeResourcesVectorDrawableRenderer对象,该类又继承了VectorDrawableRenderer,该类如下:

/**
 * Generates PNG images (and XML copies) from VectorDrawable files.
*/
public class VectorDrawableRenderer implements ResourcePreprocessor {

通过说明,可以知道该类是通过VectorDrawable文件生产PNG图片,或者拷贝 xml文件。

第二步:得到task的output目录,实际上就是/Users/zhengchao/Documents/AndroidSpace/OpenSpace/TasksPro/app/build/intermediates/res/merged/debug目录;

第三步:得到inputs文件目录集合,调用getConfiguredResourceSets(preprocessor)方法,该方法就是得到inputs路径;

第四步:生成ResourceMerger 对象。该类实现了DataMerger抽象类,主要的merge逻辑均在此类里面实现,此处不再展开,有兴趣的自行阅读:https://android.googlesource.com/platform/tools/base/+/gradle_3.0.0/sdk-common/src/main/java/com/android/ide/common/res2/DataMerger.java

第五步:mergeingLog 记录,就是intermediates/blame/res/debug目录,记录操作日志。

第六步:resourceCompiler,实际是Appt工具对象。

第七步:执行merge resource 操作

mergeDebugResources任务是把依赖的库和工程中的资源进行merge操作。

createDebugCompatibleScreenManifests

  • 准备(gradle splits配置)

在项目的app/gradle android模块下添加如下代码:

splits {
        // Screen density split settings
        density {

            // Enable or disable the density split mechanism
            enable true

            // Exclude these densities from splits
            exclude "ldpi", "tvdpi", "xxhdpi", "xxxhdpi"
        }
    }

执行命令:./gradlew createDebugCompatibleScreenManifests

  • inputs&outputs
output file:/Users/zhengchao/Documents/AndroidSpace/OpenSpace/TasksPro/app/build/intermediates/manifests/density/debug

在output目录下会生成hdpi/AndroidManigest.xml,mdpi/AndroidManigest.xml,xhdpi/AndroidManigest.xml和output.json文件。Manifest文件内容如下:




    
    
    

output.json文件内容如下(经格式化处理的):

[{
    "outputType": {
        "type": "COMPATIBLE_SCREEN_MANIFEST"
    },
    "apkInfo": {
        "type": "FULL_SPLIT",
        "splits": [{
            "filterType": "DENSITY",
            "value": "xhdpi"
        }],
        "versionCode": 1
    },
    "path": "xhdpi/AndroidManifest.xml",
    "properties": {}
}, {
    "outputType": {
        "type": "COMPATIBLE_SCREEN_MANIFEST"
    },
    "apkInfo": {
        "type": "FULL_SPLIT",
        "splits": [{
            "filterType": "DENSITY",
            "value": "hdpi"
        }],
        "versionCode": 1
    },
    "path": "hdpi/AndroidManifest.xml",
    "properties": {}
}, {
    "outputType": {
        "type": "COMPATIBLE_SCREEN_MANIFEST"
    },
    "apkInfo": {
        "type": "FULL_SPLIT",
        "splits": [{
            "filterType": "DENSITY",
            "value": "mdpi"
        }],
        "versionCode": 1
    },
    "path": "mdpi/AndroidManifest.xml",
    "properties": {}
}]
  • 源码

https://android.googlesource.com/platform/tools/base/+/gradle_3.0.0/build-system/gradle-core/src/main/java/com/android/build/gradle/tasks/CompatibleScreensManifest.java

  • 主要代码逻辑
    public void generateAll() throws IOException {
        // 1、遍历所有的density,通过generate方法生产响应的manifest文件
        // process all outputs.
        outputScope.parallelForEach(
                VariantScope.TaskOutputType.COMPATIBLE_SCREEN_MANIFEST, this::generate);
        // 2、生成json文件
        // now write the metadata file.
        outputScope.save(
                ImmutableList.of(VariantScope.TaskOutputType.COMPATIBLE_SCREEN_MANIFEST),
                outputFolder);
    }
    @Nullable
    public File generate(ApkData apkData) throws IOException {
        String density = apkData.getFilter(com.android.build.OutputFile.FilterType.DENSITY);
        if (density == null) {
            return null;
        }

        StringBuilder content = new StringBuilder();
        content.append("\n")
                .append("\n")
                .append("\n");
        if (minSdkVersion.get() != null) {
            content.append("    \n");
        }
        content.append("    \n");

        // convert unsupported values to numbers.
        density = convert(density, Density.XXHIGH, Density.XXXHIGH);

        for (String size : getScreenSizes()) {
            content.append(
                    "        \n");
        }

        content.append(
                "    \n" +
                "");


        File splitFolder = new File(outputFolder, apkData.getDirName());
        FileUtils.mkdirs(splitFolder);
        File manifestFile = new File(splitFolder, SdkConstants.ANDROID_MANIFEST_XML);

        Files.write(content.toString(), manifestFile, Charsets.UTF_8);
        return manifestFile;
    }

代码很简单第一步遍历density,然后生产相应的manifest文件。第二步生成output.json文件。

processDebugManifest

执行命令:./gradlew processDebugManifest

  • inputs&outputs
input file:/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/build/intermediates/manifests/density/debug
input file:/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/src/main/AndroidManifest.xml
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/appcompat-v7-26.1.0.aar/6b443e96f1af9aa241aaa70576c67a57/AndroidManifest.xml
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/constraint-layout-1.1.3.aar/f44da5c361a1f52801511229596f72e7/AndroidManifest.xml
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/animated-vector-drawable-26.1.0.aar/9c804d63d6f065a8f9945f9ad94fee0e/AndroidManifest.xml
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-vector-drawable-26.1.0.aar/4e56cc34abf77378e2b8d16ee237c82d/AndroidManifest.xml
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-v4-26.1.0.aar/3bf8586900bd31e222ef8b68bfd6e744/AndroidManifest.xml
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-media-compat-26.1.0.aar/267524a16ca7128dd9cef3c19f394439/AndroidManifest.xml
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-fragment-26.1.0.aar/77cf518e9868987a283f04cec221fefa/AndroidManifest.xml
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-core-utils-26.1.0.aar/8634ab1afa6a5a1a947a7bd163aba14f/AndroidManifest.xml
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-core-ui-26.1.0.aar/8902e2a864b44d47c26fbc80fdafe175/AndroidManifest.xml
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-compat-26.1.0.aar/3e4c87483eacfb4c962d7380a59a114d/AndroidManifest.xml
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/runtime-1.0.0.aar/ed085e7b9476f7a9fef4ffbb323166ba/AndroidManifest.xml
---------------------------------------------------
output file:/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/build/intermediates/manifests/instant-run/debug
output file:/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/build/intermediates/manifests/full/debug
output file:/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/build/outputs/logs/manifest-merger-debug-report.txt

根据input file路径,可知输入分为三种类型:

1、createDebugCompatibleScreenManifests 任务生成的density规则(intermediates/manifests/density/debug);

2、项目的manifest文件(TasksPro/app/src/main/AndroidManifest.xml);

3、依赖的aar包的manifest和运行环境的manifest文件(appcompat-v7-26.1.0.aar/.../AndroidManifest.xml,runtime-1.0.0.aar/.../AndroidManifest.xml)。

根据output file路径,可知输出文件也为三种:

1、instant-run模式下的输出(manifests/instant-run/debug);

2、正常模式下的输出(manifests/full/debug);

3、merge manifest 的log记录(outputs/logs/manifest-merger-debug-report.txt);

  • 源码

https://android.googlesource.com/platform/tools/base/+/gradle_3.0.0/build-system/gradle-core/src/main/java/com/android/build/gradle/tasks/MergeManifests.java

  • 主要代码逻辑

MergeManifest.java中的doFullTaskAction()方法。

    protected void doFullTaskAction() throws IOException {
        // 1、生成compatibleScreenManifests集合
        // read the output of the compatible screen manifest.
        Collection compatibleScreenManifests =
                BuildOutputs.load(
                        VariantScope.TaskOutputType.COMPATIBLE_SCREEN_MANIFEST,
                        compatibleScreensManifest);
        // 2、重新设置packageName
        String packageOverride;
        if (packageManifest != null && !packageManifest.isEmpty()) {
            packageOverride =
                    ApplicationId.load(packageManifest.getSingleFile()).getApplicationId();
        } else {
            packageOverride = getPackageOverride();
        }

        @Nullable BuildOutput compatibleScreenManifestForSplit;

        // 3、根据splits生成ApkData集合
        List splitsToGenerate =
                ProcessAndroidResources.getApksToGenerate(
                        outputScope, supportedAbis, buildTargetAbi, buildTargetDensity);
        
        // 4、遍历splitsToGenerate集合
        // FIX ME : multi threading.
        for (ApkData apkData : splitsToGenerate) {
            // 5、指定分辨率的BuildOutput对象
            compatibleScreenManifestForSplit =
                    OutputScope.getOutput(
                            compatibleScreenManifests,
                            VariantScope.TaskOutputType.COMPATIBLE_SCREEN_MANIFEST,
                            apkData);
            // 6、正常的输出
            File manifestOutputFile =
                    FileUtils.join(
                            getManifestOutputDirectory(),
                            apkData.getDirName(),
                            SdkConstants.ANDROID_MANIFEST_XML);
            // 7、instantRun 模式输出
            File instantRunManifestOutputFile =
                    FileUtils.join(
                            getInstantRunManifestOutputDirectory(),
                            apkData.getDirName(),
                            SdkConstants.ANDROID_MANIFEST_XML);
            // 8、生成mergingReport对象
            MergingReport mergingReport =
                    getBuilder()
                            .mergeManifestsForApplication(
                                    getMainManifest(),
                                    getManifestOverlays(),
                                    computeFullProviderList(compatibleScreenManifestForSplit),
                                    getFeatureName(),
                                    packageOverride,
                                    apkData.getVersionCode(),
                                    apkData.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());
            XmlDocument mergedXmlDocument =
                    mergingReport.getMergedXmlDocument(MergingReport.MergedManifestKind.MERGED);
            ImmutableMap properties =
                    mergedXmlDocument != null
                            ? ImmutableMap.of(
                                    "packageId",
                                    mergedXmlDocument.getPackageName(),
                                    "split",
                                    mergedXmlDocument.getSplitName(),
                                    SdkConstants.ATTR_MIN_SDK_VERSION,
                                    mergedXmlDocument.getMinSdkVersion())
                            : ImmutableMap.of();

            outputScope.addOutputForSplit(
                    VariantScope.TaskOutputType.MERGED_MANIFESTS,
                    apkData,
                    manifestOutputFile,
                    properties);
            outputScope.addOutputForSplit(
                    VariantScope.TaskOutputType.INSTANT_RUN_MERGED_MANIFESTS,
                    apkData,
                    instantRunManifestOutputFile,
                    properties);
        }
        // 9、存储full模式下的output.json文件
        outputScope.save(
                ImmutableList.of(VariantScope.TaskOutputType.MERGED_MANIFESTS),
                getManifestOutputDirectory());
        // 10、存储instantRun模式下的output.json文件
        outputScope.save(
                ImmutableList.of(VariantScope.TaskOutputType.INSTANT_RUN_MERGED_MANIFESTS),
                getInstantRunManifestOutputDirectory());
    }

第一步:从`createDebugCompatibleScreenManifests'任务的输出读取,生成compatibleScreenManifests集合。所以这里的值有三个(hdpi、mdpi、xhdpi)BuildOutput对象;

第二步:重新设置packageName。如果我们同时在Manifest.xml和gradle里面同时设置packageId,且id值不同,则会在此用gradle里面的package覆盖Manifest.xml里面的id值。

第三步:根据splits的配置,生成apkData集合splitsToGenerate对象;

第四步:遍历splitsToGenerate集合,生成不同的输出文件;

第五步:进入不同的split遍历,得到指定分辨率的BuildOutput对象;

第六步:正常的manifest输出文件。(/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/build/intermediates/manifests/full/debug/universal/AndroidManifest.xml

第七步:instantRun模式下的Manifest输出。

第八步:生成mergingReport对象,调用的是AndroidBuilder中的mergeManifestsForApplication()方法,该方法如下:

    /** Invoke the Manifest Merger version 2. */
    public MergingReport mergeManifestsForApplication(
            @NonNull File mainManifest,
            @NonNull List manifestOverlays,
            @NonNull List dependencies,
            @Nullable String featureName,
            String packageOverride,
            int versionCode,
            String versionName,
            @Nullable String minSdkVersion,
            @Nullable String targetSdkVersion,
            @Nullable Integer maxSdkVersion,
            @NonNull String outManifestLocation,
            @Nullable String outAaptSafeManifestLocation,
            @Nullable String outInstantRunManifestLocation,
            ManifestMerger2.MergeType mergeType,
            Map placeHolders,
            @NonNull List optionalFeatures,
            @Nullable File reportFile) {
            ...
            // 执行merge操作,调用的是ManifestMerger2类的merge()方法。
            MergingReport mergingReport = manifestMergerInvoker.merge();
            ...
    }

此任务的核心merge逻辑都在这一步,详细的细节有兴趣的同学自行看源码:https://android.googlesource.com/platform/tools/base/+/gradle_3.0.0/build-system/manifest-merger/src/main/java/com/android/manifmerger/ManifestMerger2.java?autodive=0%2F%2F%2F%2F

第九步:存储full模式下的output.json文件;

第十步:存储instantRun模式下的output.json文件

经过上面分析可知,processDebugManifest任务实际上做的是manifest文件的merge操作,将三种输入类型的manifest合成一个manifest文件,并生成相应操作日志文件。

你可能感兴趣的:(Android gradle打包涉及task源码解析(三))