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

文章序号

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

:app:preBuild UP-TO-DATE
:app:preDebugBuild UP-TO-DATE
:app:compileDebugAidl UP-TO-DATE
:app:compileDebugRenderscript UP-TO-DATE
:app:checkDebugManifest UP-TO-DATE
:app:generateDebugBuildConfig UP-TO-DATE

preDebugBuild

  • inputs&outputs
> Task :app:preDebugBuild
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/prebuild/debug
  • 源码

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

  • 主要代码逻辑
    @TaskAction
    void run() {
        // 1、compile 和 runtime的依赖包
        Set<ResolvedArtifactResult> compileArtifacts = compileManifests.getArtifacts();
        Set<ResolvedArtifactResult> runtimeArtifacts = runtimeManifests.getArtifacts();

        // 2、runtimeIds,(key:group+module,value:version)
        // Artifact 组成(group:module:version) 
        // eg:com.android.support:appcompat-v7:26.1.0,group:com.android.support,module:appcompat-v7, version:26.1.0
        // create a map where the key is either the sub-project path, or groupId:artifactId for
        // external dependencies.
        // For external libraries, the value is the version.
        Map<String, String> runtimeIds = Maps.newHashMapWithExpectedSize(runtimeArtifacts.size());

        // 3、生成runtimeIds
        // build a list of the runtime artifacts
        for (ResolvedArtifactResult artifact : runtimeArtifacts) {
            handleArtifact(artifact.getId().getComponentIdentifier(), runtimeIds::put);
        }

        // 4、对compile的依赖包进行校验(版本、依赖属性等)
        // run through the compile ones to check for provided only.
        for (ResolvedArtifactResult artifact : compileArtifacts) {
            final ComponentIdentifier compileId = artifact.getId().getComponentIdentifier();
            handleArtifact(
                    compileId,
                    (key, value) -> {
                        String runtimeVersion = runtimeIds.get(key);
                        if (runtimeVersion == null) {
                            String display = compileId.getDisplayName();
                            throw new RuntimeException(
                                    "Android dependency '"
                                            + display
                                            + "' is set to compileOnly/provided which is not supported");
                        } else if (!runtimeVersion.isEmpty()) {
                            // compare versions.
                            if (!runtimeVersion.equals(value)) {
                                throw new RuntimeException(
                                        String.format(
                                                "Android dependency '%s' has different version for the compile (%s) and runtime (%s) classpath. You should manually set the same version via DependencyResolution",
                                                key, value, runtimeVersion));
                            }
                        }
                    });
        }
    }

通过代码注释的1、2、3、4可以很明确的了解到preDebugBuild task主要是得到compile 和 runtime的依赖包并对其做一些版本号,依赖等的校验工作。

compileDebugAidl

  • inputs&outputs
> Task :app:compileDebugAidl
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-media-compat-26.1.0.aar/267524a16ca7128dd9cef3c19f394439/aidl
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-compat-26.1.0.aar/3e4c87483eacfb4c962d7380a59a114d/aidl
input file:/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/src/main/aidl/org/test/task/IMyAidlInterface.aidl
---------------------------------------------------
output file:/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/build/intermediates/incremental/compileDebugAidl
output file:/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/build/generated/source/aidl/debug

在测试工程中,有一个IMyAidlInterface.aidl文件。compileDebugAidl执行完之后在TasksPro/app/build/generated/source/aidl/debug目录下生成IMyAidlInterface.java文件,以及在TasksPro/app/build/generated/source/aidl/debug目录下生成dependency.store(记录aidl和java相关的信息)文件。

  • 源码

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

  • 主要代码逻辑

gradle配置阶段执行代码(AidlCompile->ConfigAction->execute()),主要设置输入输出等配置信息

    @Override
    public void execute(@NonNull AidlCompile compileTask) {
        final VariantConfiguration<?, ?, ?> variantConfiguration = scope
                .getVariantConfiguration();

        scope.getVariantData().aidlCompileTask = compileTask;

        compileTask.setAndroidBuilder(scope.getGlobalScope().getAndroidBuilder());
        compileTask.setVariantName(scope.getVariantConfiguration().getFullName());
        compileTask.setIncrementalFolder(scope.getIncrementalDir(getName()));

        compileTask.sourceDirs = TaskInputHelper
                .bypassFileSupplier(variantConfiguration::getAidlSourceList);
        compileTask.importDirs = scope.getArtifactFileCollection(
                COMPILE_CLASSPATH, ALL, AIDL);

        compileTask.setSourceOutputDir(scope.getAidlSourceOutputDir());

        if (variantConfiguration.getType() == VariantType.LIBRARY) {
            compileTask.setPackagedDir(scope.getPackagedAidlDir());
            compileTask.setPackageWhitelist(
                    scope.getGlobalScope().getExtension().getAidlPackageWhiteList());
        }
    }

AidlCompile.java 中的doFullTaskAction() 方法,看和核心逻辑compileAllFiles(processor);

    @Override
    protected void doFullTaskAction() throws IOException {
        .....
        
        DepFileProcessor processor = new DepFileProcessor();

        try {
            // 核心
            compileAllFiles(processor);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        .....
    }

compileAllFiles()方法调用getBuilder().compileAllAidlFiles()

    /**
     * Action methods to compile all the files.
     *
     * <p>The method receives a {@link DependencyFileProcessor} to be used by the {@link
     * com.android.builder.internal.compiler.SourceSearcher.SourceFileProcessor} during the
     * compilation.
     *
     * @param dependencyFileProcessor a DependencyFileProcessor
     */
    private void compileAllFiles(DependencyFileProcessor dependencyFileProcessor)
            throws InterruptedException, ProcessException, IOException {
        getBuilder().compileAllAidlFiles(
                sourceDirs.get(),
                getSourceOutputDir(),
                getPackagedDir(),
                getPackageWhitelist(),
                getImportDirs().getFiles(),
                dependencyFileProcessor,
                new LoggedProcessOutputHandler(getILogger()));
    }

AndroidBuilder类中的compileAllAidlFiles()方法

    /**
     * Compiles all the aidl files found in the given source folders.
     *
     * @param sourceFolders all the source folders to find files to compile
     * @param sourceOutputDir the output dir in which to generate the source code
     * @param packagedOutputDir the output dir for the AIDL files that will be packaged in an aar
     * @param packageWhiteList a list of AIDL FQCNs that are not parcelable that should also be
     *     packaged in an aar
     * @param importFolders import folders
     * @param dependencyFileProcessor the dependencyFileProcessor to record the dependencies of the
     *     compilation.
     * @throws IOException failed
     * @throws InterruptedException failed
     */
    public void compileAllAidlFiles(
            @NonNull Collection<File> sourceFolders,
            @NonNull File sourceOutputDir,
            @Nullable File packagedOutputDir,
            @Nullable Collection<String> packageWhiteList,
            @NonNull Collection<File> importFolders,
            @Nullable DependencyFileProcessor dependencyFileProcessor,
            @NonNull ProcessOutputHandler processOutputHandler)
            throws IOException, InterruptedException, ProcessException {
        ......
        
        IAndroidTarget target = mTargetInfo.getTarget();
        BuildToolInfo buildToolInfo = mTargetInfo.getBuildTools();

        // 1、aidl 工具名称
        String aidl = buildToolInfo.getPath(BuildToolInfo.PathId.AIDL);
        if (aidl == null || !new File(aidl).isFile()) {
            throw new IllegalStateException("aidl is missing from '" + aidl + "'");
        }

        List<File> fullImportList = Lists.newArrayListWithCapacity(
                sourceFolders.size() + importFolders.size());
        fullImportList.addAll(sourceFolders);
        fullImportList.addAll(importFolders);

        // 2、初始化AidlPeocessor
        AidlProcessor processor = new AidlProcessor(
                aidl,
                target.getPath(IAndroidTarget.ANDROID_AIDL),
                fullImportList,
                sourceOutputDir,
                packagedOutputDir,
                packageWhiteList,
                dependencyFileProcessor != null ?
                        dependencyFileProcessor : DependencyFileProcessor.NO_OP,
                mProcessExecutor,
                processOutputHandler);

        // 3、执行aidl工具生产相应的java文件
        for (File dir : sourceFolders) {
            DirectoryWalker.builder()
                    .root(dir.toPath())
                    .extensions("aidl")
                    .action(processor)
                    .build()
                    .walk();
        }
    }

通过上面代码分析以及输入输出文件,可以知道此task主要是找到需要编译的aidl文件,然后调用aidl工具生成相应的java接口。

compileDebugRenderscript

此任务主要是操作Renderscript,忽略;

checkDebugManifest

  • inputs&outputs
output file:/Users/zhengchao/Documents/AndroidSpace/OpenSpace/TasksPro/app/build/intermediates/check-manifest/debug
  • 源码
https://android.googlesource.com/platform/tools/base/+/gradle_3.0.0/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/tasks/CheckManifest.java
  • 主要代码逻辑

配置阶段执行代码

    @Override
    public void execute(@NonNull CheckManifest checkManifestTask) {
        scope.getVariantData().checkManifestTask = checkManifestTask;
        checkManifestTask.setVariantName(
                scope.getVariantData().getVariantConfiguration().getFullName());
        checkManifestTask.setOptional(isManifestOptional);
        // 1、得到MainManifest文件
        checkManifestTask.manifest =
                scope.getVariantData().getVariantConfiguration().getMainManifest();
        // 2、outputs路径
        checkManifestTask.fakeOutputDir =
                new File(
                        scope.getGlobalScope().getIntermediatesDir(),
                        "check-manifest/" + scope.getVariantConfiguration().getDirName());
    }

task执行逻辑,逻辑很简单,仅作简单的manifest文件校验;

    @TaskAction
    void check() {
        if (!isOptional && manifest != null && !manifest.isFile()) {
            throw new IllegalArgumentException(
                    String.format(
                            "Main Manifest missing for variant %1$s. Expected path: %2$s",
                            getVariantName(), getManifest().getAbsolutePath()));
        }
    }

通过代码可以清楚的知道,此task在配置阶段得到manifest文件,在执行阶段做一个简单的文件校验工作。
if (!isOptional && manifest != null && !manifest.isFile()) 判断是否为空,是否是文件。

generateDebugBuildConfig

  • inputs&outputs

生成BuildConfig文件

output file:/Users/zhengchao/Documents/AndroidSpace/OpenSpace/TasksPro/app/build/generated/source/buildConfig/debug
  • 源码
https://android.googlesource.com/platform/tools/base/+/gradle_3.0.0/build-system/gradle-core/src/main/java/com/android/build/gradle/tasks/GenerateBuildConfig.java
  • 主要代码逻辑

配置阶段代码GenerateBuildConfig.ConfigAction->execute(),主要就是读取gradle文件里面的一些信息(appPackageName、versionName、versionCode等)

@Override
    public void execute(@NonNull GenerateBuildConfig generateBuildConfigTask) {
        BaseVariantData variantData = scope.getVariantData();

        variantData.generateBuildConfigTask = generateBuildConfigTask;

        final GradleVariantConfiguration variantConfiguration =
                variantData.getVariantConfiguration();

        generateBuildConfigTask.setAndroidBuilder(scope.getGlobalScope().getAndroidBuilder());
        generateBuildConfigTask.setVariantName(scope.getVariantConfiguration().getFullName());

        generateBuildConfigTask.buildConfigPackageName =
                TaskInputHelper.memoize(variantConfiguration::getOriginalApplicationId);

        generateBuildConfigTask.appPackageName =
                TaskInputHelper.memoize(variantConfiguration::getApplicationId);

        generateBuildConfigTask.versionName =
                TaskInputHelper.memoize(variantConfiguration::getVersionName);
        generateBuildConfigTask.versionCode =
                TaskInputHelper.memoize(variantConfiguration::getVersionCode);

        generateBuildConfigTask.debuggable =
                TaskInputHelper.memoize(
                        () -> variantConfiguration.getBuildType().isDebuggable());

        generateBuildConfigTask.buildTypeName = variantConfiguration.getBuildType().getName();

        // no need to memoize, variant configuration does that already.
        generateBuildConfigTask.flavorName = variantConfiguration::getFlavorName;

        generateBuildConfigTask.flavorNamesWithDimensionNames =
                TaskInputHelper.memoize(variantConfiguration::getFlavorNamesWithDimensionNames);

        generateBuildConfigTask.items =
                TaskInputHelper.memoize(variantConfiguration::getBuildConfigItems);

        generateBuildConfigTask.setSourceOutputDir(scope.getBuildConfigSourceOutputDir());
    }

执行阶段,根据配置阶段读取的gradle里面关于module的信息,然后生成BuildConfig文件。

@TaskAction
    void generate() throws IOException {
        // must clear the folder in case the packagename changed, otherwise,
        // there'll be two classes.
        File destinationDir = getSourceOutputDir();
        FileUtils.cleanOutputDir(destinationDir);
        // 1、BuildConfig文件生成器
        BuildConfigGenerator generator = new BuildConfigGenerator(
                getSourceOutputDir(),
                getBuildConfigPackageName());

        // Hack (see IDEA-100046): We want to avoid reporting "condition is always true"
        // from the data flow inspection, so use a non-constant value. However, that defeats
        // the purpose of this flag (when not in debug mode, if (BuildConfig.DEBUG && ...) will
        // be completely removed by the compiler), so as a hack we do it only for the case
        // where debug is true, which is the most likely scenario while the user is looking
        // at source code.
        //map.put(PH_DEBUG, Boolean.toString(mDebug));
        // 2、生成config文件的固定信息(DEBUG、APPLICATION_ID、BUILD_TYPE、FLAVOR、VERSION_CODE、VERSION_NAME)
        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()) + '"')
                .addItems(getItems());
        // 得到flavors 数组,然后生成响应的属性到BuildConfig文件中
        List<String> flavors = getFlavorNamesWithDimensionNames();
        int count = flavors.size();
        if (count > 1) {
            for (int i = 0; i < count; i += 2) {
                generator.addField(
                        "String", "FLAVOR_" + flavors.get(i + 1), '"' + flavors.get(i) + '"');
            }
        }

        generator.generate();
    }

根据代码和输出信息,清楚的知道此task是读取gradle里面的config信息,然后生成BuildConfig.java文件。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容

  • 学习android的同学都知道android工程从使用android studio开发以后就使用了[gradle作...
    qndroid阅读 1,050评论 0 1
  • 说明 本文主要介绍和Gradle关系密切、相对不容易理解的配置,偏重概念介绍。部分内容是Android特有的(例如...
    jzj1993阅读 15,369评论 1 62
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,569评论 25 707
  • 用两张图告诉你,为什么你的 App 会卡顿? - Android - 掘金 Cover 有什么料? 从这篇文章中你...
    hw1212阅读 12,480评论 2 59
  • 几天前,画了一个孙悟空的卡通石头画,后来被同事拿走了。哈哈,没想到大家这么喜欢,看到简书里很多朋友画的石头画都那漂...
    好肚油肚_008阅读 111评论 0 0