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
- It can be seen that the task is to scan all the aidl files under the project and generate the corresponding java files
- In build dependency.store file
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
- read dependency.store If the file fails to be read, go to full operation and delete it dependency.store file
- Traverse the changedInputs file
- Add file - > compile directly
- File delete - > clean up directly
- 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
Android new configuration description
https://developer.android.com/guide/topics/renderscript/compute?hl=zh-cn