上文回顧 上篇文章里講了 android gradle plugin 的整體流程,引入插件以后生成了很多 Task,這篇文章就談談生成的這些 Task 都有什么用處,以及一些主要 Task 的實現
預備知識 理解 gradle 的基本開發 了解 gradle task 和 plugin 使用及開發 了解 android gradle plugin 的使用 看完本文可以達到什么程度 了解 android gradle plugin 中各個 task 作用 了解 android gradle plugin 中主要 task 的實現 閱讀前準備工作 1.項目添加 android gradle plugin 依賴
compile
'com.android.tools.build:gradle:3.0.1'
復制代碼 通過這種方式,可以直接依賴 plugin 的源碼,讀起來比較方便
2.官方對照源碼地址 android gradle plugin 源碼地址
大家可以直接 clone EasyGradle 項目,把 app/build.gradle 里的 implementation 'com.android.tools.build:gradle:3.0.1' 注釋打開就可以了。
在 Gradle的基本使用 和 Android Gradle Plugin 主要流程分析 里,我們知道了 gradle 中 task 的重要性,以及 android gradle plugin 的主要流程,這一篇就來分析一下 android gradle plugin 中一些重要的 task 是怎么執行的。
一、Android 打包流程 在介紹 Android Gradle Plugin Task 之前,我們先看看一個 apk 的構建流程,先放一張官方流程圖:
官方介紹的流程如下:
編譯器將您的源代碼轉換成 DEX(Dalvik Executable) 文件(其中包括 Android 設備上運行的字節碼),將所有其他內容轉換成已編譯資源。 APK 打包器將 DEX 文件和已編譯資源合并成單個 APK。 不過,必須先簽署 APK,才能將應用安裝并部署到 Android 設備上。 APK 打包器使用調試或發布密鑰庫簽署您的 APK: 在生成最終 APK 之前,打包器會使用 zipalign 工具對應用進行優化,減少其在設備上運行時占用的內存。 那么以 Task 的維度 來看 apk 的打包,是什么流程呢?我們先執行下面的命令,看一下打包一個 apk 需要哪些 task
首先我們看一下 打包一個 apk 需要哪些 task。 在項目根目錄下執行命令
./gradlew android-gradle-plugin-source:assembleDebug --console=plain
復制代碼 看一下輸出結果
:android-gradle-plugin-source:preBuild UP-TO-DATE
:android-gradle-plugin-source:preDebugBuild
:android-gradle-plugin-source:compileDebugAidl
:android-gradle-plugin-source:compileDebugRenderscript
:android-gradle-plugin-source:checkDebugManifest
:android-gradle-plugin-source:generateDebugBuildConfig
:android-gradle-plugin-source:prepareLintJar UP-TO-DATE
:android-gradle-plugin-source:generateDebugResValues
:android-gradle-plugin-source:generateDebugResources
:android-gradle-plugin-source:mergeDebugResources
:android-gradle-plugin-source:createDebugCompatibleScreenManifests
:android-gradle-plugin-source:processDebugManifest
:android-gradle-plugin-source:splitsDiscoveryTaskDebug
:android-gradle-plugin-source:processDebugResources
:android-gradle-plugin-source:generateDebugSources
:android-gradle-plugin-source:javaPreCompileDebug
:android-gradle-plugin-source:compileDebugJavaWithJavac
:android-gradle-plugin-source:compileDebugNdk NO-SOURCE
:android-gradle-plugin-source:compileDebugSources
:android-gradle-plugin-source:mergeDebugShaders
:android-gradle-plugin-source:compileDebugShaders
:android-gradle-plugin-source:generateDebugAssets
:android-gradle-plugin-source:mergeDebugAssets
:android-gradle-plugin-source:transformClassesWithDexBuilderForDebug
:android-gradle-plugin-source:transformDexArchiveWithExternalLibsDexMergerForDebug
:android-gradle-plugin-source:transformDexArchiveWithDexMergerForDebug
:android-gradle-plugin-source:mergeDebugJniLibFolders
:android-gradle-plugin-source:transformNativeLibsWithMergeJniLibsForDebug
:android-gradle-plugin-source:transformNativeLibsWithStripDebugSymbolForDebug
:android-gradle-plugin-source:processDebugJavaRes NO-SOURCE
:android-gradle-plugin-source:transformResourcesWithMergeJavaResForDebug
:android-gradle-plugin-source:validateSigningDebug
:android-gradle-plugin-source:packageDebug
:android-gradle-plugin-source:assembleDebug
復制代碼 上面就是打包一個 apk 需要的 task
二、Task 對應實現類 我們先看看每個 task 都是做什么的,以及其對應的實現類。 先回憶一下,我們在前面 android-gradle-plugin 主要流程分析里說到過,task 的實現可以在 TaskManager 里找到,創建 task 的方法主要是兩個,TaskManager.createTasksBeforeEvaluate() 和 ApplicationTaskManager.createTasksForVariantScope(),所以這些 task 的實現,也在這兩個類里找就可以,下面列出了各個 task 的作用及實現類。
Task對應實現類作用 preBuild 空 task,只做錨點使用 preDebugBuild 空 task,只做錨點使用,與 preBuild 區別是這個 task 是 variant 的錨點 compileDebugAidl AidlCompile 處理 aidl compileDebugRenderscript RenderscriptCompile 處理 renderscript checkDebugManifest CheckManifest 檢測 manifest 是否存在 generateDebugBuildConfig GenerateBuildConfig 生成 BuildConfig.java prepareLintJar PrepareLintJar 拷貝 lint jar 包到指定位置 generateDebugResValues GenerateResValues 生成 resvalues,generated.xml generateDebugResources 空 task,錨點 mergeDebugResources MergeResources 合并資源文件 createDebugCompatibleScreenManifests CompatibleScreensManifest manifest 文件中生成 compatible-screens,指定屏幕適配 processDebugManifest MergeManifests 合并 manifest 文件 splitsDiscoveryTaskDebug SplitsDiscovery 生成 split-list.json,用于 apk 分包 processDebugResources ProcessAndroidResources aapt 打包資源 generateDebugSources 空 task,錨點 javaPreCompileDebug JavaPreCompileTask 生成 annotationProcessors.json 文件 compileDebugJavaWithJavac AndroidJavaCompile 編譯 java 文件 compileDebugNdk NdkCompile 編譯 ndk compileDebugSources 空 task,錨點使用 mergeDebugShaders MergeSourceSetFolders 合并 shader 文件 compileDebugShaders ShaderCompile 編譯 shaders generateDebugAssets 空 task,錨點 mergeDebugAssets MergeSourceSetFolders 合并 assets 文件 transformClassesWithDexBuilderForDebug DexArchiveBuilderTransform class 打包 dex transformDexArchiveWithExternalLibsDexMergerForDebug ExternalLibsMergerTransform 打包三方庫的 dex,在 dex 增量的時候就不需要再 merge 了,節省時間 transformDexArchiveWithDexMergerForDebug DexMergerTransform 打包最終的 dex mergeDebugJniLibFolders MergeSouceSetFolders 合并 jni lib 文件 transformNativeLibsWithMergeJniLibsForDebug MergeJavaResourcesTransform 合并 jnilibs transformNativeLibsWithStripDebugSymbolForDebug StripDebugSymbolTransform 去掉 native lib 里的 debug 符號 processDebugJavaRes ProcessJavaResConfigAction 處理 java res transformResourcesWithMergeJavaResForDebug MergeJavaResourcesTransform 合并 java res validateSigningDebug ValidateSigningTask 驗證簽名 packageDebug PackageApplication 打包 apk assembleDebug 空 task,錨點
三、如何去讀 Task 的代碼 在 gradle plugin 中的 Task 主要有三種,一種是普通的 task,一種是增量 task,一種是 transform,下面分別看下這三種 task 怎么去讀。
如何讀 Task 的代碼 看 Task 繼承的父類,一般來說,會繼承 DefaultTask,IncrementalTask 看 @TaskAction 注解的方法,此方法就是這個 Task 做的事情 如何讀 IncrementalTask 我們先看看下這個類,這個類表示的是增量 Task,什么是增量呢?是相對于 全量來說的,全量我們可以理解為調用 clean 以后第一次編譯的過程,這個就是全量編譯,之后修改了代碼或者資源文件,再次編譯,就是增量編譯。 其中比較重要的幾個方法如下:
public abstract class IncrementalTask extends BaseTask {@Internal protected boolean isIncremental() {
return false ;}
protected abstract void doFullTaskAction()
throws Exception;
protected void doIncrementalTaskAction(Map<File, FileStatus> changedInputs)
throws Exception {}
@TaskAction void taskAction(IncrementalTaskInputs inputs)
throws Exception {
if (
this .isIncremental() && inputs.isIncremental()) {
this .doIncrementalTaskAction(
this .getChangedInputs(inputs));}
else {
this .getProject().getLogger().info(
"Unable do incremental execution: full task run" );
this .doFullTaskAction();}}
private Map<File, FileStatus> getChangedInputs(IncrementalTaskInputs inputs) {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) -> {FileStatus var10000 = (FileStatus)changedInputs.put(change.getFile(), FileStatus.REMOVED);});
return changedInputs;}
}
復制代碼 簡單介紹了 IncrementalTask 之后,我們這里強調一下,如何去讀一個 增量 Task 的代碼,主要有四步:
首先這個 Task 要繼承 IncrementalTask, 其次看 isIncremental 方法,如果返回 true,說明支持增量,返回 false 則不支持 然后看 doFullTaskAction 方法,是全量的時候執行的操作 最后看 doIncrementalTaskAction 方法,這里是增量的時候執行的操作 如何讀 Transform 繼承自 Transform 看其 transform 方法的實現 四、重點 Task 實現分析 上面每個 task 已經簡單說明了具體做什么以及對應的實現類,下面選了幾個比較重要的來分析一下其實現 為什么分析這幾個呢?這幾個代表了 gradle 自動生成代碼,資源的處理,以及 dex 的處理,算是 apk 打包過程中比較重要的幾環。 generateDebugBuildConfig processDebugManifest mergeDebugResources processDebugResources transformClassesWithDexBuilderForDebug transformDexArchiveWithExternalLibsDexMergerForDebug transformDexArchiveWithDexMergerForDebug
分析過程主要下面幾個步驟,實現類,整體實現圖,調用鏈路(方便以后回看代碼),以及重要代碼分析
4.1 generateDebugBuildConfig 4.1.1 實現類 GenerateBuildConfig
4.1.2 整體實現圖
4.1.3 代碼調用鏈路 GenerateBuildConfig.generate -> BuildConfigGenerator.generate -> JavaWriter
復制代碼 4.1.4 主要代碼分析 在 GenerateBuildConfig 中,主要生成代碼的步驟如下:
生成 BuildConfigGenerator 添加默認的屬性,包括 DEBUG,APPLICATION_ID,FLAVOR,VERSION_CODE,VERSION_NAME 添加自定義屬性 調用 JavaWriter 生成 BuildConfig.java 文件
@TaskAction
void generate () throws IOException {BuildConfigGenerator generator =
new BuildConfigGenerator(getSourceOutputDir(),getBuildConfigPackageName());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()); 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();
}
復制代碼 4.2 mergeDebugResources 4.2.1 實現類 MergeResources
4.2.2 整體實現圖
4.2.3 調用鏈路 MergeResources.doFullTaskAction -> ResourceMerger.mergeData -> MergedResourceWriter.end -> QueueableAapt2.compile -> Aapt2QueuedResourceProcessor.compile -> AaptProcess.compile -> AaptV2CommandBuilder.makeCompile
復制代碼 4.2.4 主要代碼分析 MergeResources 這個類,繼承自 IncrementalTask,按照前面說的閱讀增量 Task 代碼的步驟,依次看三個方法的實現:isIncremental,doFullTaskAction,doIncrementalTaskAction
protected boolean isIncremental() {
return true ;}
復制代碼 通過 getConfiguredResourceSets() 獲取 resourceSets,包括了自己的 res/ 和 依賴庫的 res/ 以及 build/generated/res/rs
List<ResourceSet> resourceSets = getConfiguredResourceSets(preprocessor);
復制代碼 創建 ResourceMerger
ResourceMerger merger =
new ResourceMerger(minSdk);
復制代碼 創建 QueueableResourceCompiler,因為 gradle3.x 以后支持了 aapt2,所以這里有兩種選擇 aapt 和 aapt2。其中 aapt2 有三種模式,OutOfProcessAaptV2,AaptV2Jni,QueueableAapt2,這里默認創建了 QueueableAapt2,resourceCompiler = QueueableAapt2
QueueableResourceCompiler resourceCompiler =makeAapt(aaptGeneration,getBuilder(),fileCache,crunchPng,variantScope,getAaptTempDir(),mergingLog)
復制代碼 將第一步獲取的 resourceSet 加入 ResourceMerger 中 for (ResourceSet
resourceSet : resourceSets) {resourceSet.loadFromFiles(getILogger());merger.addDataSet(resourceSet);
}
復制代碼 創建 MergedResourceWriter 調用 ResourceMerger.mergeData 合并資源
merger.mergeData(writer,
false );
復制代碼 調用 MergedResourceWriter 的 start(),addItem(),end() 方法,偽代碼如下:
consumer.start()
for item
in sourceSets: consumer.addItem(item)
consumer.end()
復制代碼 調用 QueueableAapt2 -> Aapt2QueuedResourceProcessor -> AaptProcess 處理資源
Future<File> result =
this .mResourceCompiler.compile(
new CompileResourceRequest(fileToCompile, request.getOutput(), request.getFolderName(),
this .pseudoLocalesEnabled,
this .crunchPng));
public void compile(
@NonNull CompileResourceRequest request,
@NonNull Job<AaptProcess> job,
@Nullable ProcessOutputHandler processOutputHandler)
throws IOException {mWriter.write(joiner.join(AaptV2CommandBuilder.makeCompile(request)));mWriter.flush();
}
復制代碼 這一步調用 aapt2 命令去處理資源,處理完以后 xxx.xml.flat 格式
doIncrementalTaskAction 增量任務過程和全量其實差異不大,只不過是在獲取 resourceSets 的時候,使用的是修改后的文件 4.3 processDebugResources 4.3.1 實現類 ProcessAndroidResources
4.3.2 整體實現圖
4.3.3 調用鏈路 ProcessAndroidResources.doFullTaskAction -> ProcessAndroidResources.invokeAaptForSplit -> AndroidBuilder.processResources -> QueueAapt2.link -> Aapt2QueuedResourceProcessor.link -> AaptProcess.link -> AaptV2CommandBuilder.makeLink
復制代碼 4.3.4 主要代碼分析 ProcessAndroidResources 也是繼承自 IncrementalTask,但是沒有重寫 isIncremental,所以不是增量的 Task,直接看 doFullTaskAction 即可
doFullTaskAction 這個里面代碼雖然多,但是主要的邏輯比較簡單,就是調用 aapt2 link 去生成資源包。 這里會處理 splits apk 相關的內容,關于 splits apk 具體可以查看 splits apk,簡單來說,就是可以按照屏幕分辨率,abis 來生成不同的 apk,從而讓特定用戶的安裝包變小。 分下面幾個步驟: 獲取 split 數據 List<ApkData> splitsToGenerate =getApksToGenerate(outputScope, supportedAbis, buildTargetAbi, buildTargetDensity);
復制代碼 返回的是一個 ApkData 列表,ApkData 有三個子類,分別是 Main,Universal,FullSplit 我們配置 如下:
android {splits {density {enable
true exclude
"ldpi" ,
"xxhdpi" ,
"xxxhdpi" compatibleScreens
'small' ,
'normal' ,
'large' ,
'xlarge' }}
}
復制代碼 這里的 ApkData 會返回一個 Universal 和多個 FullSplit,Universal 代表的是主 apk,FullSplit 就是根據屏幕密度拆分的 apk。 如果我們沒有配置 splits apk,那么這里只會返回一個 Main 的實例,標識完整的 apk。 2. 先處理 main 和 不依賴 density 的 ApkData 資源
List<ApkData> apkDataList =
new ArrayList<>(splitsToGenerate);
for (ApkData
apkData : splitsToGenerate) {
if (apkData.requiresAapt()) {
boolean codeGen =(apkData.getType() == OutputFile.OutputType.MAIN|| apkData.getFilter(OutputFile.FilterType.DENSITY) ==
null );
if (codeGen) {apkDataList.remove(apkData);invokeAaptForSplit(manifestsOutputs,libraryInfoList,packageIdFileSet,splitList,featureResourcePackages,apkData,codeGen,aapt);
break ;}}
}
復制代碼 調用 invokeAaptForSplit 處理資源
void invokeAaptForSplit(...) {String packageForR =
null ;File srcOut =
null ;File symbolOutputDir =
null ;File proguardOutputFile =
null ;File mainDexListProguardOutputFile =
null ;
if (generateCode) {packageForR = originalApplicationId;srcOut = getSourceOutputDir();
if (srcOut !=
null ) {FileUtils.cleanOutputDir(srcOut);}symbolOutputDir = textSymbolOutputDir.get();proguardOutputFile = getProguardOutputFile();mainDexListProguardOutputFile = getMainDexListProguardOutputFile();}getBuilder().processResources(aapt, config);
}
復制代碼 調用 AndroidBuilder.processResources -> QueueAapt2.link -> Aapt2QueuedResourceProcessor.link -> AaptProcess.link -> AaptV2CommandBuilder.makeLink 處理資源,生成資源包以及 R.java 文件 處理其他 ApkData 資源,這里只會生成資源包而不會生成 R.java 文件 關于 aapt2 的 compile 和 link 參數,可以在 developer.android.com/studio/comm… 這里看
4.4 processDebugManifest 4.4.1 實現類 MergeManifests
4.4.2 整體實現圖
4.4.3 調用鏈路 MergeManifests.dofFullTaskAction -> AndroidBuilder.mergeManifestsForApplication -> Invoker.merge -> ManifestMerge2.merge
復制代碼 4.4.4 主要代碼分析 MergeManifests 也是繼承了 IncrementalTask,但是沒有實現 isIncremental,所以只看其 doFullTaskAction 即可。 這個 task 功能主要是合并 mainfest,包括 module 和 flavor 里的,整個過程通過 MergingReport,ManifestMerger2 和 XmlDocument 進行。 這里直接看 ManifestMerger2.merge() 的 merge 過程 。 主要有幾個步驟:
獲取依賴庫的 manifest 信息,用 LoadedManifestInfo 標識 獲取主 module 的 manifest 信息 替換主 module 的 Manifest 中定義的某些屬性,替換成 gradle 中定義的屬性 例如: package, version_code, version_name, min_sdk_versin 等等 performSystemPropertiesInjection(mergingReportBuilder, xmlDocumentOptional.get());
protected void performSystemPropertiesInjection(
@NonNull MergingReport.Builder mergingReport,
@NonNull XmlDocument xmlDocument) {
for (ManifestSystemProperty
manifestSystemProperty : ManifestSystemProperty.values()) {String propertyOverride = mSystemPropertyResolver.getValue(manifestSystemProperty);
if (propertyOverride !=
null ) {manifestSystemProperty.addTo(mergingReport.getActionRecorder(), xmlDocument, propertyOverride);}}
}
復制代碼 合并 flavor,buildType 中的 manifest for (File inputFile : mFlavorsAndBuildTypeFiles) {LoadedManifestInfo overlayDocument = load(new ManifestInfo(null, inputFile, XmlDocument.Type.OVERLAY,Optional.of(mainPackageAttribute.get().getValue())),selectors,mergingReportBuilder);// 檢查 package 定義Optional<XmlAttribute> packageAttribute =overlayDocument.getXmlDocument().getPackage();if (loadedMainManifestInfo.getOriginalPackageName().isPresent() &&packageAttribute.isPresent()&& !loadedMainManifestInfo.getOriginalPackageName().get().equals(packageAttribute.get().getValue())) {// 如果 package 定義重復的話,會輸出下面信息,我們平時應該或多或少見過類似的錯誤String message = mMergeType == MergeType.APPLICATION? String.format("Overlay manifest:package attribute declared at %1$s value=(%2$s)\n"+ "\thas a different value=(%3$s) "+ "declared in main manifest at %4$s\n"+ "\tSuggestion: remove the overlay declaration at %5$s "+ "\tand place it in the build.gradle:\n"+ "\t\tflavorName {\n"+ "\t\t\tapplicationId = \"%2$s\"\n"+ "\t\t}",packageAttribute.get().printPosition(),packageAttribute.get().getValue(),mainPackageAttribute.get().getValue(),mainPackageAttribute.get().printPosition(),packageAttribute.get().getSourceFile().print(true)): String.format("Overlay manifest:package attribute declared at %1$s value=(%2$s)\n"+ "\thas a different value=(%3$s) "+ "declared in main manifest at %4$s",packageAttribute.get().printPosition(),packageAttribute.get().getValue(),mainPackageAttribute.get().getValue(),mainPackageAttribute.get().printPosition());// ...return mergingReportBuilder.build();}
}
復制代碼 合并依賴庫的 manifest for (LoadedManifestInfo
libraryDocument : loadedLibraryDocuments) {mLogger.verbose(
"Merging library manifest " + libraryDocument.getLocation());xmlDocumentOptional = merge(xmlDocumentOptional, libraryDocument, mergingReportBuilder);
if (!xmlDocumentOptional.isPresent()) {
return mergingReportBuilder.build();}
}
復制代碼 處理 manifest 的 placeholders performPlaceHolderSubstitution(loadedMainManifestInfo, xmlDocumentOptional.get(), mergingReportBuilder, severity);
復制代碼 之后對最終合并后的 manifest 中的一些屬性重新進行一次替換,類似步驟 4 保存 manifest 到 build/intermediates/manifest/fullxxx/AndroidManifest.xml 這就生成了最終的 Manifest 文件 4.5 transformClassesWithDexBuilderForDebug 4.5.1 實現類 DexArchiveBuilderTransform
4.5.2 整體實現圖
4.5.3 調用鏈路 DexArchiveBuilderTransform.transform -> DexArchiveBuilderTransform.convertJarToDexArchive -> DexArchiveBuilderTransform.convertToDexArchive -> DexArchiveBuilderTransform.launchProcessing -> DxDexArchiveBuilder.convert
復制代碼 4.5.4 主要代碼分析 在 DexArchiveBuilderTransform 中,對 class 的處理分為兩種方式,一種是對 目錄下的 class 進行處理,一種是對 .jar 里的 class 進行處理。 為什么要分為這兩種方式呢?.jar 中的 class 一般來說都是依賴庫,基本上不會改變,gradle 在這里做了一個緩存,但是兩種方式最終都會調用到 convertToDexArchive,可以說是殊途同歸吧。
convertJarToDexArchive 處理 jar 處理 .jar 時,會對 jar 包中的每一個 class 都單獨打成一個 .dex 文件,之后還是放在 .jar 包中 private List<File> convertJarToDexArchive(
@NonNull Context context,
@NonNull JarInput toConvert,
@NonNull TransformOutputProvider transformOutputProvider)
throws Exception {File cachedVersion = cacheHandler.getCachedVersionIfPresent(toConvert);
if (cachedVersion ==
null ) {
return convertToDexArchive(context, toConvert, transformOutputProvider,
false );}
else {File outputFile = getPreDexJar(transformOutputProvider, toConvert,
null );Files.copy(cachedVersion.toPath(),outputFile.toPath(),StandardCopyOption.REPLACE_EXISTING);
return ImmutableList.of();}}
復制代碼 convertToDexArchive 處理 dir 以及 jar 的后續處理 對 dir 處理使用 convertToDexArchive 其中會調用 launchProcessing private static void launchProcessing(
@NonNull DexConversionParameters dexConversionParameters,
@NonNull OutputStream outStream,
@NonNull OutputStream errStream)
throws IOException, URISyntaxException {
boolean hasIncrementalInfo =dexConversionParameters.isDirectoryBased() && dexConversionParameters.isIncremental;Predicate<String> toProcess =hasIncrementalInfo? path -> {Map<File, Status> changedFiles =((DirectoryInput) dexConversionParameters.input).getChangedFiles();File resolved = inputPath.resolve(path).toFile();Status status = changedFiles.get(resolved);return status == Status.ADDED || status == Status.CHANGED;}: path ->
true ;bucketFilter = bucketFilter.and(toProcess);
try (ClassFileInput input = ClassFileInputs.fromPath(inputPath)) {dexArchiveBuilder.convert(input.entries(bucketFilter),Paths.get(
new URI(dexConversionParameters.output)),dexConversionParameters.isDirectoryBased());}
catch (DexArchiveBuilderException ex) {
throw new DexArchiveBuilderException(
"Failed to process " + inputPath.toString(), ex);}}
復制代碼 在 launchProcessing 中,有下面幾個步驟:
判斷目錄下的 class 是否新增或者修改過 調用 DexArchiveBuilder.build 去處理修改過的 class DexArchiveBuilder 有兩個子類,D8DexArchiveBuilder 和 DxDexArchiveBuilder,分別是調用 d8 和 dx 去打 dex 4.6 transformDexArchiveWithExternalLibsDexMergerForDebug 4.6.1 實現類 ExternalLibsMergerTransform
4.6.2 整體實現圖
4.6.3 調用鏈路 這一步是處理依賴庫的 dex,把上一步生成的依賴庫的 dex merge 成一個 dex
// dx
ExternalLibsMergerTransform.transform -> DexMergerTransformCallable.call -> DxDexArchiveMerger.mergeDexArchives -> DxDexArchiveMerger.mergeMonoDex -> DexArchiveMergerCallable.call -> DexMerger.merge
復制代碼 // d8
ExternalLibsMergerTransform.transform -> DexMergerTransformCallable.call -> D8DexArchiveMerger.mergeDexArchives -> 調用 D8 命令
復制代碼 這里邏輯比較簡單,就不具體分析了
4.7 transformDexArchiveWithDexMergerForDebug 4.7.1 實現類 DexMergerTransform
4.7.2 整體實現圖
4.7.3 調用鏈路 和上一步類似
// dx
DexMergerTransform.transform -> DexMergerTransform.handleLegacyAndMonoDex -> DexMergerTransformCallable.call -> DxDexArchiveMerger.mergeDexArchives -> DxDexArchiveMerger.mergeMonoDex -> DexArchiveMergerCallable.call -> DexMerger.merge
復制代碼 // d8
DexMergerTransform.transform -> DexMergerTransform.handleLegacyAndMonoDex -> DexMergerTransformCallable.call -> D8DexArchiveMerger.mergeDexArchives -> 調用 D8 命令
復制代碼 五、本文重點 Android Gradle Plugin 中各個 Task 的作用及實現類,具體可參考文中第二節「Task 對應實現類」 如何閱讀 Task 的代 【Android 修煉手冊】系列內容 每周更新 歡迎關注下面賬號,獲取更新: 微信搜索公眾號: ZYLAB Github 掘金
總結
以上是生活随笔 為你收集整理的【Android 修炼手册】Gradle 篇 -- Android Gradle Plugin 主要 Task 分析 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。