Android build process - Part 2

Android build process - Part 2

Pre operation

In order to illustrate the task s executed by gradle during Android construction, a simple demo is written

git clone https://github.com/xiaobaoyihao/AndroidGradleTaskDemo.git

List the tasks in the Android build process first

task list

demo under clone, executed by terminal

./gradlew assembleDebug --console=plain

Output the following task chain

:app:checkDebugClasspath UP-TO-DATE
: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
:app:prepareLintJar UP-TO-DATE
:app:mainApkListPersistenceDebug UP-TO-DATE
:app:generateDebugResValues UP-TO-DATE
:app:generateDebugResources UP-TO-DATE
:app:mergeDebugResources UP-TO-DATE
:app:createDebugCompatibleScreenManifests UP-TO-DATE
:app:processDebugManifest UP-TO-DATE
:app:splitsDiscoveryTaskDebug UP-TO-DATE
:app:processDebugResources UP-TO-DATE
:app:generateDebugSources UP-TO-DATE
:app:javaPreCompileDebug UP-TO-DATE
:app:compileDebugJavaWithJavac UP-TO-DATE
:app:compileDebugNdk NO-SOURCE
:app:compileDebugSources UP-TO-DATE
:app:mergeDebugShaders UP-TO-DATE
:app:compileDebugShaders UP-TO-DATE
:app:generateDebugAssets UP-TO-DATE
:app:mergeDebugAssets UP-TO-DATE
:app:transformClassesWithDexBuilderForDebug UP-TO-DATE
:app:transformDexArchiveWithExternalLibsDexMergerForDebug UP-TO-DATE
:app:transformDexArchiveWithDexMergerForDebug UP-TO-DATE
:app:mergeDebugJniLibFolders UP-TO-DATE
:app:transformNativeLibsWithMergeJniLibsForDebug UP-TO-DATE
:app:checkDebugLibraries UP-TO-DATE
:app:processDebugJavaRes NO-SOURCE
:app:transformResourcesWithMergeJavaResForDebug UP-TO-DATE
:app:validateSigningDebug UP-TO-DATE
:app:packageDebug UP-TO-DATE
:app:assembleDebug UP-TO-DATE

In order to observe each task more clearly, we can add log printing to the input and output of each task, and release it in demo build.gradle Middle task print area code

How to view a task class

It can be found by taskname. Generally, most task paths are found in

com.android.build.gradle.internal.tasks
com.android.build.gradle.tasks

Under the directory, the task name is basically the same as the class name

Task1: checkDebugClasspath

1. input/output

input:/Users/apple/.gradle/caches/transforms-1/files-1.1/appcompat-v7-26.1.0.aar/2774ea4f1cf1e83a6ad8e8d3c8b463b6/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/constraint-layout-1.1.3.aar/f43c0ba95b6494825ed940fc4f04662b/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/animated-vector-drawable-26.1.0.aar/559112320064089dfaf6780e71d5b44f/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-vector-drawable-26.1.0.aar/c2c3ad4abfd49316f6769b8238b0f010/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-v4-26.1.0.aar/9ac5f97e8ccb24c52b7cbb6202c12ad0/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-media-compat-26.1.0.aar/53ab5ad72634f3497309a8788f3ca200/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-fragment-26.1.0.aar/7e6a4ce6591d722d47aafc36d980f8b4/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-core-utils-26.1.0.aar/4c474caa9ac1f01c4936bd96905ecacd/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-core-ui-26.1.0.aar/868eaa7e0c620cd85d72ad4f340e8bb1/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-compat-26.1.0.aar/4ec3c1c46e5bad9ac3b91f45a2afec3e/jars/classes.jar
input:/Users/apple/.gradle/caches/modules-2/files-2.1/com.android.support/support-annotations/26.1.0/814258103cf26a15fcc26ecce35f5b7d24b73f8/support-annotations-26.1.0.jar
input:/Users/apple/.gradle/caches/modules-2/files-2.1/com.android.support.constraint/constraint-layout-solver/1.1.3/bde0667d7414c16ed62d3cfe993cff7f9d732373/constraint-layout-solver-1.1.3.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/runtime-1.0.0.aar/5b2333922ba05b1f174de51739b24d14/jars/classes.jar
input:/Users/apple/.gradle/caches/modules-2/files-2.1/android.arch.lifecycle/common/1.0.0/e414a4cb28434e25c4f6aa71426eb20cf4874ae9/common-1.0.0.jar
input:/Users/apple/.gradle/caches/modules-2/files-2.1/android.arch.core/common/1.0.0/a2d487452376193fc8c103dd2b9bd5f2b1b44563/common-1.0.0.jar
=========================================================
output:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/checkDebugClasspath/debug

It can be inferred from the task literal that the task is to verify the app classpath. What kind is it?
We found a ppClasspathCheckTask.java How to make sure it is correct?
We found that there is a ConfigAction.getName method


In fact, the return string of this method is the name of the executed task (checkDebugClasspath). You can see the specific process directly from the code

Where ConfigAction is called

Core entry code

//ApplicationTaskManager.java
@Override
  protected Task createVariantPreBuildTask(@NonNull VariantScope scope) {
      final VariantType variantType = scope.getVariantConfiguration().getType();

      if (variantType.isApk()) {
          AppClasspathCheckTask classpathCheck =
                  taskFactory.create(new AppClasspathCheckTask.ConfigAction(scope));

          return (variantType.isTestComponent()
                          ? taskFactory.create(new TestPreBuildTask.ConfigAction(scope))
                          : taskFactory.create(new AppPreBuildTask.ConfigAction(scope)))
                  .dependsOn(classpathCheck);
      }

      return super.createVariantPreBuildTask(scope);
  }

Some people will ask how to get more bugs in the middle? Tracing the internal implementation of getName method to find the final call BaseVariantData.getTaskName method

public String getTaskName(@NonNull String prefix, @NonNull String suffix) {
    return StringHelper.appendCapitalized(prefix, variantConfiguration.getFullName(), suffix);
}

// VariantConfiguration.java
 public String getFullName() {
     if (mFullName == null) {
         mFullName =
                 computeFullName(
                         getFlavorName(),
                         mBuildType,
                         mType,
                         mTestedConfig == null ? null : mTestedConfig.getType());
     }

     return mFullName;
 }
 /**
     * Returns the full, unique name of the variant in camel case (starting with a lower case),
     * including BuildType, Flavors and Test (if applicable).
     *
     * @param flavorName the flavor name, as computed by {@link #computeFlavorName(List)}
     * @param buildType the build type
     * @param type the variant type
     * @return the name of the variant
     */
 public static <B extends BuildType> String computeFullName(
            @NonNull String flavorName,
            @NonNull B buildType,
            @NonNull VariantType type,
            @Nullable VariantType testedType) {
     StringBuilder sb = new StringBuilder();

     if (!flavorName.isEmpty()) {
         sb.append(flavorName);
         StringHelper.appendCapitalized(sb, buildType.getName());
     } else {
         sb.append(buildType.getName());
     }

     if (type.isHybrid()) {
         sb.append("Feature");
     }

     if (type.isTestComponent()) {
         if (testedType != null && testedType.isHybrid()) {
             sb.append("Feature");
         }
         sb.append(type.getSuffix());
     }
     return sb.toString();
 }

It can be seen that computeFullName returns the variant related name, which matches the assemblydebug we entered, so we can find the corresponding ConfigAction class directly through the Task name in the later stage, so the real Task class name can also be found

2. How to find the task implementation class

Determine whether the return value of getName matches the task name in all subclasses of TaskConfigAction - > corresponding task implementation class

3. Core class (AppClasspathCheckTask)

//AppClasspathCheckTask.java
@TaskAction
void run() {
     compareClasspaths();
 }
 
//ClasspathComparisionTask.java
void compareClasspaths() {
	//com.android.support:appcompat-v7:23.3.0
	//group:module/artifact:version
	
     Set<ResolvedArtifactResult> runtimeArtifacts = runtimeClasspath.getArtifacts();
     Set<ResolvedArtifactResult> compileArtifacts = compileClasspath.getArtifacts();

     // Store a map of groupId -> (artifactId -> versions)
     Map<String, Map<String, String>> runtimeIds =
             Maps.newHashMapWithExpectedSize(runtimeArtifacts.size());
	
	//1. Store the runtime dependent version information to groupid - > versions in the map
     for (ResolvedArtifactResult artifact : runtimeArtifacts) {
         // only care about external dependencies to compare versions.
         final ComponentIdentifier componentIdentifier =
                 artifact.getId().getComponentIdentifier();
         if (componentIdentifier instanceof ModuleComponentIdentifier) {
             ModuleComponentIdentifier moduleId =
                     (ModuleComponentIdentifier) componentIdentifier;

             // get the sub-map, creating it if needed.
             Map<String, String> subMap =
                     runtimeIds.computeIfAbsent(moduleId.getGroup(), s -> new HashMap<>());

             subMap.put(moduleId.getModule(), moduleId.getVersion());
         }
     }
	
	//Traverse the compileArtifacts collection, and the same group.module Compare, how to find version inconsistency, call ondiferentversingsfound method
     for (ResolvedArtifactResult artifact : compileArtifacts) {
         // only care about external dependencies to compare versions.
         final ComponentIdentifier componentIdentifier =
                 artifact.getId().getComponentIdentifier();
         if (componentIdentifier instanceof ModuleComponentIdentifier) {
             ModuleComponentIdentifier moduleId =
                     (ModuleComponentIdentifier) componentIdentifier;

             Map<String, String> subMap = runtimeIds.get(moduleId.getGroup());
             if (subMap == null) {
                 continue;
             }

             String runtimeVersion = subMap.get(moduleId.getModule());
             if (runtimeVersion == null) {
                 continue;
             }

             if (runtimeVersion.equals(moduleId.getVersion())) {
                 continue;
             }

             onDifferentVersionsFound(
                     moduleId.getGroup(),
                     moduleId.getModule(),
                     runtimeVersion,
                     moduleId.getVersion());
         }
     }
 }

//AppClasspathCheckTask.java
@Override
void onDifferentVersionsFound(
         @NonNull String group,
         @NonNull String module,
         @NonNull String runtimeVersion,
         @NonNull String compileVersion) {
	
	//This method is very simple. If the comparison versions are different, it will prompt that there is a conflict in the dependency, which may cause crash at runtime
     String suggestedVersion;
     try {
         GradleVersion runtime = GradleVersion.parse(runtimeVersion);
         GradleVersion compile = GradleVersion.parse(compileVersion);
         if (runtime.compareTo(compile) > 0) {
             suggestedVersion = runtimeVersion;
         } else {
             suggestedVersion = compileVersion;
         }
     } catch (Throwable e) {
         // in case we are unable to parse versions for some reason, choose runtime
         suggestedVersion = runtimeVersion;
     }

     String message =
             String.format(
                     "Conflict with dependency '%1$s:%2$s' in project '%3$s'. Resolved versions for "
                             + "runtime classpath (%4$s) and compile classpath (%5$s) differ. This "
                             + "can lead to runtime crashes. To resolve this issue follow "
                             + "advice at https://developer.android.com/studio/build/gradle-tips#configure-project-wide-properties. "
                             + "Alternatively, you can try to fix the problem "
                             + "by adding this snippet to %6$s:\n"
                             + "dependencies {\n"
                             + "    implementation(\"%1$s:%2$s:%7$s\")\n"
                             + "}\n",
                     group,
                     module,
                     getProject().getPath(),
                     runtimeVersion,
                     compileVersion,
                     getProject().getBuildFile(),
                     suggestedVersion);

     reporter.reportWarning(EvalIssueReporter.Type.GENERIC, message);
 }

Summary:

The task is to verify the compiled classpath and the runtime classpath, if they are the same group.module If there are different version s in, users will be prompted that there are conflicts in their dependencies, which will result in crash

See the official website for conflict resolution

Task2: preDebugBuild

1. input/ouput

taskName:preDebugBuild
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/appcompat-v7-26.1.0.aar/2774ea4f1cf1e83a6ad8e8d3c8b463b6/AndroidManifest.xml
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/constraint-layout-1.1.3.aar/f43c0ba95b6494825ed940fc4f04662b/AndroidManifest.xml
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/animated-vector-drawable-26.1.0.aar/559112320064089dfaf6780e71d5b44f/AndroidManifest.xml
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-vector-drawable-26.1.0.aar/c2c3ad4abfd49316f6769b8238b0f010/AndroidManifest.xml
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-v4-26.1.0.aar/9ac5f97e8ccb24c52b7cbb6202c12ad0/AndroidManifest.xml
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-media-compat-26.1.0.aar/53ab5ad72634f3497309a8788f3ca200/AndroidManifest.xml
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-fragment-26.1.0.aar/7e6a4ce6591d722d47aafc36d980f8b4/AndroidManifest.xml
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-core-utils-26.1.0.aar/4c474caa9ac1f01c4936bd96905ecacd/AndroidManifest.xml
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-core-ui-26.1.0.aar/868eaa7e0c620cd85d72ad4f340e8bb1/AndroidManifest.xml
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-compat-26.1.0.aar/4ec3c1c46e5bad9ac3b91f45a2afec3e/AndroidManifest.xml
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/runtime-1.0.0.aar/5b2333922ba05b1f174de51739b24d14/AndroidManifest.xml
=========================================================
output:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/prebuild/debug

The input is the list file of the dependency library, and the output is empty. This task is also to verify the application variants

2. Core class (AppPreBuildTask)

@TaskAction
void run() {
    Set<ResolvedArtifactResult> compileArtifacts = new HashSet<>();
    compileArtifacts.addAll(compileManifests.getArtifacts());
    compileArtifacts.addAll(compileNonNamespacedManifests.getArtifacts());

    Set<ResolvedArtifactResult> runtimeArtifacts = new HashSet<>();
    runtimeArtifacts.addAll(runtimeManifests.getArtifacts());
    runtimeArtifacts.addAll(runtimeNonNamespacedManifests.getArtifacts());

    // 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());

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

    // run through the compile ones to check for provided only.
    for (ResolvedArtifactResult artifact : compileArtifacts) {
        final ComponentIdentifier compileId = artifact.getId().getComponentIdentifier();
        handleArtifact(
                compileId,
                (key, value) -> {
                	//Check code logic
                    String runtimeVersion = runtimeIds.get(key);
                    if (runtimeVersion == null) {
                        if (isBaseModule) {
                            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));
                        }
                    }
                });
    }
}

private void handleArtifact(
            @NonNull ComponentIdentifier id, @NonNull BiConsumer<String, String> consumer) {
    if (id instanceof ProjectComponentIdentifier) {
         consumer.accept(((ProjectComponentIdentifier) id).getProjectPath().intern(), "");
     } else if (id instanceof ModuleComponentIdentifier) {
         ModuleComponentIdentifier moduleComponentId = (ModuleComponentIdentifier) id;
         consumer.accept(
                 moduleComponentId.getGroup() + ":" + moduleComponentId.getModule(),
                 moduleComponentId.getVersion());
     } else if (id instanceof OpaqueComponentArtifactIdentifier) {
         // skip those for now.
         // These are file-based dependencies and it's unlikely to be an AAR.
     } else {
         getLogger()
                 .warn(
                         "Unknown ComponentIdentifier type: "
                                 + id.getClass().getCanonicalName());
     }
 }

If you decorate aar with compileOnly and provider, it will fail

To verify our idea, add the following code to app.build.gradle in

compileOnly 'com.facebook.stetho:stetho:1.5.0'

implement

./gradlew preDebugBuild

results of enforcement

Can backpush compileOnly does not support decoration aar only supports jar
In addition, AppClasspathCheckTask is the precondition of AppPreBuildTask task. In the 👇 The figure of

Task3: compileDebugAidl

1. input/output

taskName:compileDebugAidl
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-media-compat-26.1.0.aar/53ab5ad72634f3497309a8788f3ca200/aidl
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-compat-26.1.0.aar/4ec3c1c46e5bad9ac3b91f45a2afec3e/aidl
input:/Users/apple/work/project/AndroidGradleTaskDemo/app/src/main/aidl/com/gradle/task/demo/IHelloAidlInterface.aidl
=========================================================
output:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/incremental/compileDebugAidl
output:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/generated/source/aidl/debug
  1. It can be seen that the task is to scan all the aidl files under the project and generate the corresponding java files
  2. In build dependency.store file
input output remarks
*.aidl build/*.java java source file
- dependency.store . Aidl - >. Java mapping file table

2. Core class (AidlCompile)

Let's first look at the inheritance relationship of this class
AidlCompile -> IncrementalTask(abstract)

//IncrementalTask.java
/**
 * Gradle's entry-point into this task. Determines whether or not it's possible to do this task
 * incrementally and calls either doIncrementalTaskAction() if an incremental build is possible,
 * and doFullTaskAction() if not.
 */
@TaskAction
void taskAction(IncrementalTaskInputs inputs) throws Exception {
	//If it is a non incremental operation, directly follow the full operation method
    if (!isIncremental() || !inputs.isIncremental()) {
        getProject().getLogger().info("Unable do incremental execution: full task run");
		//Give to subclass implementation
        doFullTaskAction();
        return;
    }
	
	//Take the incremental operation method, and take the changed input file to the subclass for implementation
    doIncrementalTaskAction(getChangedInputs(inputs));
}

private Map<File, FileStatus> getChangedInputs(IncrementalTaskInputs inputs) {
   final Map<File, FileStatus> changedInputs = Maps.newHashMap();

    inputs.outOfDate(
            change -> {
                FileStatus status = change.isAdded() ? FileStatus.NEW : FileStatus.CHANGED;
                changedInputs.put(change.getFile(), status);
            });

    inputs.removed(change -> changedInputs.put(change.getFile(), FileStatus.REMOVED));

    return changedInputs;
}

It can be seen that the main process logic of the task is relatively simple. If it is a full action, use the doFullTaskAction method. Otherwise, use the incremental doincrementaltask action method. Let's take a look at the full action first

doFullTaskAction

/** Task to compile aidl files. Supports incremental update. */
@CacheableTask
public class AidlCompile extends IncrementalTask {
	private static final String DEPENDENCY_STORE = "dependency.store";
    private static final PatternSet PATTERN_SET = new PatternSet().include("**/*.aidl");
    
	@Override
	protected void doFullTaskAction() throws IOException {
	
		//1. Clean the output directory before full operation
	    // this is full run, clean the previous output
	    File destinationDir = getSourceOutputDir();
	    File parcelableDir = getPackagedDir();
	    FileUtils.cleanOutputDir(destinationDir);
	    if (parcelableDir != null) {
	        FileUtils.cleanOutputDir(parcelableDir);
	    }
		
		//2. Compile all aidl file
	    DepFileProcessor processor = new DepFileProcessor();
	    try {
	        compileAllFiles(processor);
	    } catch (Exception e) {
	        throw new RuntimeException(e);
	    }
		
		//3. generate aid->java Mapping files
	    List<DependencyData> dataList = processor.getDependencyDataList();
	
	    DependencyDataStore store = new DependencyDataStore();
	    store.addData(dataList);
	    try {
	        store.saveTo(new File(getIncrementalFolder(), DEPENDENCY_STORE));
	    } catch (IOException e) {
	        throw new RuntimeException(e);
	    }
	}
	@InputFiles
	@SkipWhenEmpty
	@PathSensitive(PathSensitivity.RELATIVE)
	public FileTree getSourceFiles() {
	    // this is because aidl may be in the same folder as Java and we want to restrict to
	    // .aidl files and not java files.
	    return getProject().files(sourceDirs.get()).getAsFileTree().matching(PATTERN_SET);
	}
}

@InputFiles
@SkipWhenEmpty
@PathSensitive(PathSensitivity.RELATIVE)
public FileTree getSourceFiles() {
    // this is because aidl may be in the same folder as Java and we want to restrict to
    // .aidl files and not java files.
    return getProject().files(sourceDirs.get()).getAsFileTree().matching(PATTERN_SET);
}	

You can see that the key method is compileAllFiles, which is to call the aidl tool to generate java source files. Go in and have a look

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 {
    //1. Verify input parameters
    checkNotNull(sourceFolders, "sourceFolders cannot be null.");
    checkNotNull(sourceOutputDir, "sourceOutputDir cannot be null.");
    checkNotNull(importFolders, "importFolders cannot be null.");
    checkState(mTargetInfo != null,
            "Cannot call compileAllAidlFiles() before setTargetInfo() is called.");

    IAndroidTarget target = mTargetInfo.getTarget();
    BuildToolInfo buildToolInfo = mTargetInfo.getBuildTools();
	
	//2., the key point is to get the aidl tool path (first determine the buildTool version, and find the aidl toolkit from the buildTool toolkit, see below).
    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);
	
	//3. Build an aidl processor to generate java files
    AidlProcessor processor = new AidlProcessor(
            aidl,
            target.getPath(IAndroidTarget.ANDROID_AIDL),
            fullImportList,
            sourceOutputDir,
            packagedOutputDir,
            packageWhiteList,
            dependencyFileProcessor != null ?
                    dependencyFileProcessor : DependencyFileProcessor.NO_OP,
            mProcessExecutor,
            processOutputHandler);
	
	//4. Traverse the input aidl file set, and perform compilation once
    for (File dir : sourceFolders) {
        DirectoryWalker.builder()
                .root(dir.toPath())
                .extensions("aidl")
                .action(processor)
                .build()
                .walk();
    }
}

Operation process

DirectoryWalker.walk -> AidlProcessor.call -> GradleProcessExecutor.execute

Stick it down AidlProcessor.call Partial code

public void call(@NonNull Path startDir, @NonNull Path path) throws IOException {
   ProcessInfoBuilder builder = new ProcessInfoBuilder();

    builder.setExecutable(mAidlExecutable);

    builder.addArgs("-p" + mFrameworkLocation);
    builder.addArgs("-o" + mSourceOutputDir.getAbsolutePath());

    // add all the library aidl folders to access parcelables that are in libraries
    for (File f : mImportFolders) {
        builder.addArgs("-I" + f.getAbsolutePath());
    }

    // create a temp file for the dependency
    File depFile = File.createTempFile("aidl", ".d");
    builder.addArgs("-d" + depFile.getAbsolutePath());

    builder.addArgs(path.toAbsolutePath().toString());

    ProcessResult result = mProcessExecutor.execute(
            builder.createProcess(), mProcessOutputHandler);

    ...
}

3. Mapping file

The third part is generation dependency.store Documents, this is over, we are interested in their own good.
Incremental operation is divided into several steps

  1. read dependency.store If the file fails to be read, go to full operation and delete it dependency.store file
  2. Traverse the changedInputs file
    1. Add file - > compile directly
    2. File delete - > clean up directly
    3. File modification - > read all the options that the file depends on and compile them

Dependent task: preBuildTask

public AidlCompile createAidlTask(@NonNull VariantScope scope) {
   AidlCompile aidlCompileTask = taskFactory.create(new AidlCompile.ConfigAction(scope));
    scope.getTaskContainer().setAidlCompileTask(aidlCompileTask);
    scope.getTaskContainer().getSourceGenTask().dependsOn(aidlCompileTask);
	//Rely on preBuildTask
    aidlCompileTask.dependsOn(scope.getTaskContainer().getPreBuildTask());

    return aidlCompileTask;
}

Task4: compileDebugRenderscript

1. RenderScript overview

RenderScript is a framework for running compute intensive tasks with high performance on Android. RenderScript is mainly used for data parallel computing, but serial workloads can also benefit. The RenderScript runtime can schedule work in parallel between multiple processors provided on the device, such as multi-core CPU s and GPU s. This allows you to focus on expressing algorithms rather than scheduling work. RenderScript is particularly useful for performing image processing, computational photography, or computer vision applications.

2. input/ouput

taskName:compileDebugRenderscript
input:/Users/apple/work/project/AndroidGradleTaskDemo/app/src/debug/rs
input:/Users/apple/work/project/AndroidGradleTaskDemo/app/src/main/rs
=========================================================
output:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/rs/debug/lib
output:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/rs/debug/obj
output:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/generated/res/rs/debug
output:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/generated/source/rs/debug

3. Renderscriptcompile

Routine is similar to task 3. Call llvm RS CC to generate the source file. However, there is no such tool class in package 28.02, so the toolkit with 28.0.2 should fail to compile, but it has not been tried. 30.0.0 exists

Core code

@TaskAction
void taskAction() throws IOException, InterruptedException, ProcessException {

	 // 1. Pre operation and cleaning function
     // this is full run (always), clean the previous outputs
     File sourceDestDir = getSourceOutputDir();
     FileUtils.cleanOutputDir(sourceDestDir);

     File resDestDir = getResOutputDir();
     FileUtils.cleanOutputDir(resDestDir);

     File objDestDir = getObjOutputDir();
     FileUtils.cleanOutputDir(objDestDir);

     File libDestDir = getLibOutputDir();
     FileUtils.cleanOutputDir(libDestDir);

     Set<File> sourceDirectories = sourceDirs.getFiles();
		
	 //2. Compile all rs files. The routine is similar to aidl
     getBuilder()
             .compileAllRenderscriptFiles(
                     sourceDirectories,
                     getImportFolders(),
                     sourceDestDir,
                     resDestDir,
                     objDestDir,
                     libDestDir,
                     getTargetApi(),
                     isDebugBuild(),
                     getOptimLevel(),
                     isNdkMode(),
                     isSupportMode(),
                     useAndroidX(),
                     getNdkConfig() == null ? null : getNdkConfig().getAbiFilters(),
                     new LoggedProcessOutputHandler(getILogger()));
 }

Compile critical code

The specific details will not be explained any more. We are interested in looking at the source code ourselves

Reference link

https://developer.android.com/studio/build/dependencies#resolution_errors

Limitations of compileOnly

Android new configuration description

https://developer.android.com/guide/topics/renderscript/compute?hl=zh-cn

Tags: Gradle Java Android xml

Posted on Thu, 25 Jun 2020 22:42:13 -0400 by jason102178