美文网首页Gradle
Android gradle打包涉及task源码解析(二)

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

作者: 枫吹一痕 | 来源:发表于2018-11-15 12:30 被阅读0次

    文章序号

    此篇文章将分析如下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文件。

    相关文章

      网友评论

        本文标题:Android gradle打包涉及task源码解析(二)

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