Android構建流程——篇二

預操作

爲說明Android構建過程中gradle執行的各task,寫了簡單demo

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

先羅列下Android構建流程中任務有哪些,本系列講解都是基於gradle plugin 3.2.0源碼

任務列表

clone下demo,終端執行

./gradlew assembleDebug --console=plain

輸出如下任務鏈

: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

爲了更清楚觀察各任務,我們可以對每個任務的輸入和輸出添加日誌打印,在demo中放開build.gradle中任務打印區域代碼

如何查看一個task類

可以通過taskname來查找,一般任務路徑大部分都是在

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

目錄下,任務名稱基本和類名一致

在這裏插入圖片描述

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

從task字面可以猜測出該任務是對app classpath做校驗,具體是在那個類呢?
我們找到到AppClasspathCheckTask.java,如何確定是正確的呢?
我們發現該類中有個ConfigAction.getName方法

在這裏插入圖片描述
其實這個方法的返回字符串就是執行的任務名(checkDebugClasspath),具體怎麼流程大家直接看代碼就知道了

ConfigAction被調用的地方

核心入口代碼

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

有人會問中間的debug怎麼多出來?追蹤getName方法內部實現發現最終調用BaseVariantData.getTaskName方法

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

可以看到computeFullName是返回variant相關的名字,和我們輸入的assembleDebug相匹配,所以我們後期直接通過任務名就能準確找到對應ConfigAction類了這樣真正的Task類名也就找到了

2. 如何找到任務實現類

確定任務名 -> 在所有TaskConfigAction的子類中尋找getName返回值是否與其task名匹配 -> 對應Task實現類

3. 核心類(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. 存儲runtime依賴版本信息到map中groupId -> (artifactId -> versions)
     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());
         }
     }
	
	//對compileArtifacts集合進行遍歷,並和compileArtifacts中相同的group.module比較,如何發現版本不一致,調用onDifferentVersionsFound方法
     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) {
	
	//這個方法很簡單,就是比較版本不一樣的話,會提示依賴有衝突,運行時可能導致crash
     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);
 }

簡單總結:

該任務就是對編譯類路徑和運行時類路徑進行校驗,如果相同的group.module中存在不同version,則提示用戶依賴有衝突,會導致運行是crash

衝突解決方案參見官網

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

輸入都爲依賴庫的清單文件,輸出爲空;這個任務簡單說也是對應用變體做校驗

2. 核心類(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) -> {
                	//校驗代碼邏輯
                    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());
     }
 }

如果用compileOnly、provider修飾aar則會失敗

驗證下我們的想法,add 如下代碼到app.build.gradle中

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

執行

./gradlew preDebugBuild

執行結果
在這裏插入圖片描述
可以反推compileOnly不支持修飾aar只支持jar
補充下AppClasspathCheckTask是AppPreBuildTask任務前置條件,在👇的圖可以體現出來

在這裏插入圖片描述

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. 可以看出該任務是掃描工程下依賴的所有aidl文件,並生成對應的java文件
  2. 在生成dependency.store文件
    在這裏插入圖片描述 在這裏插入圖片描述
input output 備註
*.aidl build/*.java java源文件
- dependency.store .aidl -> .java映射文件表

2. 核心類(AidlCompile)

先來看下該類繼承關係
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 (!isIncremental() || !inputs.isIncremental()) {
        getProject().getLogger().info("Unable do incremental execution: full task run");
		//交給子類實現
        doFullTaskAction();
        return;
    }
	
	//走增量操作方法,並攜帶變更的輸入文件,交給子類實現
    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;
}

可以看到該任務主流程邏輯還是比較簡單,如果是全量動作,走doFullTaskAction方法,否則走doIncrementalTaskAction增量操作,我們先來看下全量操作

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. 全量操作前,先對輸出目錄做下清理操作
	    // this is full run, clean the previous output
	    File destinationDir = getSourceOutputDir();
	    File parcelableDir = getPackagedDir();
	    FileUtils.cleanOutputDir(destinationDir);
	    if (parcelableDir != null) {
	        FileUtils.cleanOutputDir(parcelableDir);
	    }
		
		//2. 編譯所有aidl文件
	    DepFileProcessor processor = new DepFileProcessor();
	    try {
	        compileAllFiles(processor);
	    } catch (Exception e) {
	        throw new RuntimeException(e);
	    }
		
		//3. 生成aid->java映射文件
	    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);
}	

可以看到關鍵方法是compileAllFiles它是調用aidl工具生成了java源文件,進去看下

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. 校驗輸入參數
    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. 關鍵點,獲取aidl工具路徑(先確定buildTool版本,在從buildTool工具包中找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);
	
	//3. 構建一個aidl處理器,用來生成java文件
    AidlProcessor processor = new AidlProcessor(
            aidl,
            target.getPath(IAndroidTarget.ANDROID_AIDL),
            fullImportList,
            sourceOutputDir,
            packagedOutputDir,
            packageWhiteList,
            dependencyFileProcessor != null ?
                    dependencyFileProcessor : DependencyFileProcessor.NO_OP,
            mProcessExecutor,
            processOutputHandler);
	
	//4. 對輸入的aidl文件集合進行遍歷,一次執行編譯操作
    for (File dir : sourceFolders) {
        DirectoryWalker.builder()
                .root(dir.toPath())
                .extensions("aidl")
                .action(processor)
                .build()
                .walk();
    }
}

操作流程

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

貼下AidlProcessor.call部分代碼

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. 映射文件

簡單梳理下,第三部就是生成dependency.store文件了,這個就過了,大家有興趣自己看好啦。
增量操作這裏簡單說下主要分幾步操作

  1. 讀取dependency.store文件,如果讀取失敗,直接走全量操作,並刪除dependency.store文件
  2. 對changedInputs文件進行遍歷判斷
    1. 文件新增 -> 直接進行編譯處理
    2. 文件是刪除 -> 直接進行清理操作
    3. 文件修改 -> 讀取文件依賴的所有選項,並對其進行編譯處理

依賴任務:preBuildTask

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

    return aidlCompileTask;
}

Task4: compileDebugRenderscript

1. RenderScript 概覽

RenderScript 是用於在 Android 上以高性能運行計算密集型任務的框架。RenderScript 主要用於數據並行計算,不過串行工作負載也可以從中受益。RenderScript 運行時可在設備上提供的多個處理器(如多核 CPU 和 GPU)間並行調度工作。這樣您就能夠專注於表達算法而不是調度工作。RenderScript 對於執行圖像處理、計算攝影或計算機視覺的應用來說尤其有用。

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)

套路和任務3差不多,調用工具(llvm-rs-cc)生成源文件

核心代碼

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

	 // 1.預操作,清理作用
     // 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. 編譯所有rs文件,套路和aidl差不多
     getBuilder()
             .compileAllRenderscriptFiles(
                     sourceDirectories,
                     getImportFolders(),
                     sourceDestDir,
                     resDestDir,
                     objDestDir,
                     libDestDir,
                     getTargetApi(),
                     isDebugBuild(),
                     getOptimLevel(),
                     isNdkMode(),
                     isSupportMode(),
                     useAndroidX(),
                     getNdkConfig() == null ? null : getNdkConfig().getAbiFilters(),
                     new LoggedProcessOutputHandler(getILogger()));
 }

編譯關鍵代碼
在這裏插入圖片描述

具體細節不再闡述了,大家有興趣自己看源碼

參考鏈接

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

compileOnly的限制

Android新配置說明

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章