日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

【Android 修炼手册】Gradle 篇 -- Android Gradle Plugin 主要 Task 分析

發(fā)布時間:2023/12/15 Android 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【Android 修炼手册】Gradle 篇 -- Android Gradle Plugin 主要 Task 分析 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

上文回顧

上篇文章里講了 android gradle plugin 的整體流程,引入插件以后生成了很多 Task,這篇文章就談?wù)勆傻倪@些 Task 都有什么用處,以及一些主要 Task 的實現(xiàn)

預(yù)備知識

  • 理解 gradle 的基本開發(fā)
  • 了解 gradle task 和 plugin 使用及開發(fā)
  • 了解 android gradle plugin 的使用
  • 看完本文可以達到什么程度

  • 了解 android gradle plugin 中各個 task 作用
  • 了解 android gradle plugin 中主要 task 的實現(xiàn)
  • 閱讀前準備工作

    1.項目添加 android gradle plugin 依賴

    compile 'com.android.tools.build:gradle:3.0.1' 復(fù)制代碼

    通過這種方式,可以直接依賴 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 是怎么執(zhí)行的。

    一、Android 打包流程

    在介紹 Android Gradle Plugin Task 之前,我們先看看一個 apk 的構(gòu)建流程,先放一張官方流程圖:

    官方介紹的流程如下:

  • 編譯器將您的源代碼轉(zhuǎn)換成 DEX(Dalvik Executable) 文件(其中包括 Android 設(shè)備上運行的字節(jié)碼),將所有其他內(nèi)容轉(zhuǎn)換成已編譯資源。
  • APK 打包器將 DEX 文件和已編譯資源合并成單個 APK。 不過,必須先簽署 APK,才能將應(yīng)用安裝并部署到 Android 設(shè)備上。
  • APK 打包器使用調(diào)試或發(fā)布密鑰庫簽署您的 APK:
  • 在生成最終 APK 之前,打包器會使用 zipalign 工具對應(yīng)用進行優(yōu)化,減少其在設(shè)備上運行時占用的內(nèi)存。
  • 那么以 Task 的維度來看 apk 的打包,是什么流程呢?我們先執(zhí)行下面的命令,看一下打包一個 apk 需要哪些 task

    首先我們看一下 打包一個 apk 需要哪些 task。 在項目根目錄下執(zhí)行命令

    ./gradlew android-gradle-plugin-source:assembleDebug --console=plain 復(fù)制代碼

    看一下輸出結(jié)果

    :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 復(fù)制代碼

    上面就是打包一個 apk 需要的 task

    二、Task 對應(yīng)實現(xiàn)類

    我們先看看每個 task 都是做什么的,以及其對應(yīng)的實現(xiàn)類。
    先回憶一下,我們在前面 android-gradle-plugin 主要流程分析里說到過,task 的實現(xiàn)可以在 TaskManager 里找到,創(chuàng)建 task 的方法主要是兩個,TaskManager.createTasksBeforeEvaluate() 和 ApplicationTaskManager.createTasksForVariantScope(),所以這些 task 的實現(xiàn),也在這兩個類里找就可以,下面列出了各個 task 的作用及實現(xiàn)類。

    Task對應(yīng)實現(xiàn)類作用
    preBuild空 task,只做錨點使用
    preDebugBuild空 task,只做錨點使用,與 preBuild 區(qū)別是這個 task 是 variant 的錨點
    compileDebugAidlAidlCompile處理 aidl
    compileDebugRenderscriptRenderscriptCompile處理 renderscript
    checkDebugManifestCheckManifest檢測 manifest 是否存在
    generateDebugBuildConfigGenerateBuildConfig生成 BuildConfig.java
    prepareLintJarPrepareLintJar拷貝 lint jar 包到指定位置
    generateDebugResValuesGenerateResValues生成 resvalues,generated.xml
    generateDebugResources空 task,錨點
    mergeDebugResourcesMergeResources合并資源文件
    createDebugCompatibleScreenManifestsCompatibleScreensManifestmanifest 文件中生成 compatible-screens,指定屏幕適配
    processDebugManifestMergeManifests合并 manifest 文件
    splitsDiscoveryTaskDebugSplitsDiscovery生成 split-list.json,用于 apk 分包
    processDebugResourcesProcessAndroidResourcesaapt 打包資源
    generateDebugSources空 task,錨點
    javaPreCompileDebugJavaPreCompileTask生成 annotationProcessors.json 文件
    compileDebugJavaWithJavacAndroidJavaCompile編譯 java 文件
    compileDebugNdkNdkCompile編譯 ndk
    compileDebugSources空 task,錨點使用
    mergeDebugShadersMergeSourceSetFolders合并 shader 文件
    compileDebugShadersShaderCompile編譯 shaders
    generateDebugAssets空 task,錨點
    mergeDebugAssetsMergeSourceSetFolders合并 assets 文件
    transformClassesWithDexBuilderForDebugDexArchiveBuilderTransformclass 打包 dex
    transformDexArchiveWithExternalLibsDexMergerForDebugExternalLibsMergerTransform打包三方庫的 dex,在 dex 增量的時候就不需要再 merge 了,節(jié)省時間
    transformDexArchiveWithDexMergerForDebugDexMergerTransform打包最終的 dex
    mergeDebugJniLibFoldersMergeSouceSetFolders合并 jni lib 文件
    transformNativeLibsWithMergeJniLibsForDebugMergeJavaResourcesTransform合并 jnilibs
    transformNativeLibsWithStripDebugSymbolForDebugStripDebugSymbolTransform去掉 native lib 里的 debug 符號
    processDebugJavaResProcessJavaResConfigAction處理 java res
    transformResourcesWithMergeJavaResForDebugMergeJavaResourcesTransform合并 java res
    validateSigningDebugValidateSigningTask驗證簽名
    packageDebugPackageApplication打包 apk
    assembleDebug空 task,錨點

    三、如何去讀 Task 的代碼

    在 gradle plugin 中的 Task 主要有三種,一種是普通的 task,一種是增量 task,一種是 transform,下面分別看下這三種 task 怎么去讀。

    如何讀 Task 的代碼

  • 看 Task 繼承的父類,一般來說,會繼承 DefaultTask,IncrementalTask
  • 看 @TaskAction 注解的方法,此方法就是這個 Task 做的事情
  • 如何讀 IncrementalTask

    我們先看看下這個類,這個類表示的是增量 Task,什么是增量呢?是相對于 全量來說的,全量我們可以理解為調(diào)用 clean 以后第一次編譯的過程,這個就是全量編譯,之后修改了代碼或者資源文件,再次編譯,就是增量編譯。
    其中比較重要的幾個方法如下:

    public abstract class IncrementalTask extends BaseTask {// ...@Internalprotected boolean isIncremental() { // 是否需要增量,默認是 falsereturn false;}// 需要子類實現(xiàn),全量的時候執(zhí)行的任務(wù)protected abstract void doFullTaskAction() throws Exception;// 增量的時候執(zhí)行的任務(wù),默認是什么都不執(zhí)行,參數(shù)是增量的時候修改過的文件protected void doIncrementalTaskAction(Map<File, FileStatus> changedInputs) throws Exception {}@TaskActionvoid 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;} } 復(fù)制代碼

    簡單介紹了 IncrementalTask 之后,我們這里強調(diào)一下,如何去讀一個 增量 Task 的代碼,主要有四步:

  • 首先這個 Task 要繼承 IncrementalTask,
  • 其次看 isIncremental 方法,如果返回 true,說明支持增量,返回 false 則不支持
  • 然后看 doFullTaskAction 方法,是全量的時候執(zhí)行的操作
  • 最后看 doIncrementalTaskAction 方法,這里是增量的時候執(zhí)行的操作
  • 如何讀 Transform

  • 繼承自 Transform
  • 看其 transform 方法的實現(xiàn)
  • 四、重點 Task 實現(xiàn)分析

    上面每個 task 已經(jīng)簡單說明了具體做什么以及對應(yīng)的實現(xiàn)類,下面選了幾個比較重要的來分析一下其實現(xiàn)
    為什么分析這幾個呢?這幾個代表了 gradle 自動生成代碼,資源的處理,以及 dex 的處理,算是 apk 打包過程中比較重要的幾環(huán)。
    generateDebugBuildConfig
    processDebugManifest
    mergeDebugResources
    processDebugResources
    transformClassesWithDexBuilderForDebug
    transformDexArchiveWithExternalLibsDexMergerForDebug
    transformDexArchiveWithDexMergerForDebug

    分析過程主要下面幾個步驟,實現(xiàn)類,整體實現(xiàn)圖,調(diào)用鏈路(方便以后回看代碼),以及重要代碼分析

    4.1 generateDebugBuildConfig

    4.1.1 實現(xiàn)類

    GenerateBuildConfig

    4.1.2 整體實現(xiàn)圖

    4.1.3 代碼調(diào)用鏈路
    GenerateBuildConfig.generate -> BuildConfigGenerator.generate -> JavaWriter 復(fù)制代碼
    4.1.4 主要代碼分析

    在 GenerateBuildConfig 中,主要生成代碼的步驟如下:

  • 生成 BuildConfigGenerator
  • 添加默認的屬性,包括 DEBUG,APPLICATION_ID,FLAVOR,VERSION_CODE,VERSION_NAME
  • 添加自定義屬性
  • 調(diào)用 JavaWriter 生成 BuildConfig.java 文件
  • // GenerateBuildConfig.generate() @TaskAction void generate() throws IOException {// ...BuildConfigGenerator generator = new BuildConfigGenerator(getSourceOutputDir(),getBuildConfigPackageName());// 添加默認的屬性,包括 DEBUG,APPLICATION_ID,FLAVOR,VERSION_CODE,VERSION_NAMEgenerator.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) + '"');}}// 內(nèi)部調(diào)用 JavaWriter 生成 java 文件generator.generate(); } 復(fù)制代碼

    4.2 mergeDebugResources

    4.2.1 實現(xiàn)類

    MergeResources

    4.2.2 整體實現(xiàn)圖

    4.2.3 調(diào)用鏈路
    MergeResources.doFullTaskAction -> ResourceMerger.mergeData -> MergedResourceWriter.end -> QueueableAapt2.compile -> Aapt2QueuedResourceProcessor.compile -> AaptProcess.compile -> AaptV2CommandBuilder.makeCompile 復(fù)制代碼
    4.2.4 主要代碼分析

    MergeResources 這個類,繼承自 IncrementalTask,按照前面說的閱讀增量 Task 代碼的步驟,依次看三個方法的實現(xiàn):isIncremental,doFullTaskAction,doIncrementalTaskAction

    • isIncremental
    // 說明 Task 支持增量protected boolean isIncremental() {return true;} 復(fù)制代碼
    • doFullTaskAction
  • 通過 getConfiguredResourceSets() 獲取 resourceSets,包括了自己的 res/ 和 依賴庫的 res/ 以及 build/generated/res/rs
  • // MergeResources.doFullTaskAction() List<ResourceSet> resourceSets = getConfiguredResourceSets(preprocessor); 復(fù)制代碼
  • 創(chuàng)建 ResourceMerger
  • // MergeResources.doFullTaskAction() ResourceMerger merger = new ResourceMerger(minSdk); 復(fù)制代碼
  • 創(chuàng)建 QueueableResourceCompiler,因為 gradle3.x 以后支持了 aapt2,所以這里有兩種選擇 aapt 和 aapt2。其中 aapt2 有三種模式,OutOfProcessAaptV2,AaptV2Jni,QueueableAapt2,這里默認創(chuàng)建了 QueueableAapt2,resourceCompiler = QueueableAapt2
  • // MergeResources.doFullTaskAction() // makeAapt 中會判斷使用 aapt 還是 aapt2,這里以 aapt2 為例,返回的是 QueueableAapt2 對象 QueueableResourceCompiler resourceCompiler =makeAapt(aaptGeneration,getBuilder(),fileCache,crunchPng,variantScope,getAaptTempDir(),mergingLog) 復(fù)制代碼
  • 將第一步獲取的 resourceSet 加入 ResourceMerger 中
  • for (ResourceSet resourceSet : resourceSets) {resourceSet.loadFromFiles(getILogger());merger.addDataSet(resourceSet); } 復(fù)制代碼
  • 創(chuàng)建 MergedResourceWriter
  • 調(diào)用 ResourceMerger.mergeData 合并資源
  • // MergeResources.doFullTaskAction() merger.mergeData(writer, false /*doCleanUp*/); 復(fù)制代碼
  • 調(diào)用 MergedResourceWriter 的 start(),addItem(),end() 方法,偽代碼如下:
  • // DataMerger.mergeData consumer.start() for item in sourceSets:// item 包括了需要處理的資源,包括 xml 和 圖片資源,每一個 item 對應(yīng)的文件,會創(chuàng)建一個 CompileResourceRequest 對象,加入到 mCompileResourceRequests 里consumer.addItem(item) consumer.end() 復(fù)制代碼
  • 調(diào)用 QueueableAapt2 -> Aapt2QueuedResourceProcessor -> AaptProcess 處理資源
  • // MergedResourceWriter.end() Future<File> result = this.mResourceCompiler.compile(new CompileResourceRequest(fileToCompile, request.getOutput(), request.getFolderName(), this.pseudoLocalesEnabled, this.crunchPng)); // AaptProcess.compile public void compile(@NonNull CompileResourceRequest request,@NonNull Job<AaptProcess> job,@Nullable ProcessOutputHandler processOutputHandler)throws IOException {// ... // 使用 AaptV2CommandBuilder 生成 aapt2 命令mWriter.write(joiner.join(AaptV2CommandBuilder.makeCompile(request)));mWriter.flush(); // 輸出命令 } 復(fù)制代碼

    這一步調(diào)用 aapt2 命令去處理資源,處理完以后 xxx.xml.flat 格式

    • doIncrementalTaskAction
      增量任務(wù)過程和全量其實差異不大,只不過是在獲取 resourceSets 的時候,使用的是修改后的文件

    4.3 processDebugResources

    4.3.1 實現(xiàn)類

    ProcessAndroidResources

    4.3.2 整體實現(xiàn)圖

    4.3.3 調(diào)用鏈路
    ProcessAndroidResources.doFullTaskAction -> ProcessAndroidResources.invokeAaptForSplit -> AndroidBuilder.processResources -> QueueAapt2.link -> Aapt2QueuedResourceProcessor.link -> AaptProcess.link -> AaptV2CommandBuilder.makeLink 復(fù)制代碼
    4.3.4 主要代碼分析

    ProcessAndroidResources 也是繼承自 IncrementalTask,但是沒有重寫 isIncremental,所以不是增量的 Task,直接看 doFullTaskAction 即可

    • doFullTaskAction
      這個里面代碼雖然多,但是主要的邏輯比較簡單,就是調(diào)用 aapt2 link 去生成資源包。
      這里會處理 splits apk 相關(guān)的內(nèi)容,關(guān)于 splits apk 具體可以查看 splits apk,簡單來說,就是可以按照屏幕分辨率,abis 來生成不同的 apk,從而讓特定用戶的安裝包變小。
      分下面幾個步驟:
  • 獲取 split 數(shù)據(jù)
  • List<ApkData> splitsToGenerate =getApksToGenerate(outputScope, supportedAbis, buildTargetAbi, buildTargetDensity); 復(fù)制代碼

    返回的是一個 ApkData 列表,ApkData 有三個子類,分別是 Main,Universal,FullSplit
    我們配置 如下:

    android {splits {// Configures multiple APKs based on screen density.density {// Configures multiple APKs based on screen density.enable true// Specifies a list of screen densities Gradle should not create multiple APKs for.exclude "ldpi", "xxhdpi", "xxxhdpi"// Specifies a list of compatible screen size settings for the manifest.compatibleScreens 'small', 'normal', 'large', 'xlarge'}} } 復(fù)制代碼

    這里的 ApkData 會返回一個 Universal 和多個 FullSplit,Universal 代表的是主 apk,FullSplit 就是根據(jù)屏幕密度拆分的 apk。
    如果我們沒有配置 splits apk,那么這里只會返回一個 Main 的實例,標識完整的 apk。
    2. 先處理 main 和 不依賴 density 的 ApkData 資源

    // ProcessAndroidResources.doFullTaskAction List<ApkData> apkDataList = new ArrayList<>(splitsToGenerate); for (ApkData apkData : splitsToGenerate) {if (apkData.requiresAapt()) {// 這里只處理 main 和不依賴 density 的資源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;}} } 復(fù)制代碼
  • 調(diào)用 invokeAaptForSplit 處理資源
  • // ProcessAndroidResources.invokeAaptForSplit void invokeAaptForSplit(...) {// ...String packageForR = null;File srcOut = null;File symbolOutputDir = null;File proguardOutputFile = null;File mainDexListProguardOutputFile = null;// 如果傳了 generateCode 參數(shù),會生成 R.java if (generateCode) {packageForR = originalApplicationId;// we have to clean the source folder output in case the package name changed.srcOut = getSourceOutputDir();if (srcOut != null) {FileUtils.cleanOutputDir(srcOut);}symbolOutputDir = textSymbolOutputDir.get();proguardOutputFile = getProguardOutputFile();mainDexListProguardOutputFile = getMainDexListProguardOutputFile();}// ...getBuilder().processResources(aapt, config); } 復(fù)制代碼
  • 調(diào)用 AndroidBuilder.processResources -> QueueAapt2.link -> Aapt2QueuedResourceProcessor.link -> AaptProcess.link -> AaptV2CommandBuilder.makeLink 處理資源,生成資源包以及 R.java 文件
  • 處理其他 ApkData 資源,這里只會生成資源包而不會生成 R.java 文件
  • 關(guān)于 aapt2 的 compile 和 link 參數(shù),可以在 developer.android.com/studio/comm… 這里看

    4.4 processDebugManifest

    4.4.1 實現(xiàn)類

    MergeManifests

    4.4.2 整體實現(xiàn)圖

    4.4.3 調(diào)用鏈路
    MergeManifests.dofFullTaskAction -> AndroidBuilder.mergeManifestsForApplication -> Invoker.merge -> ManifestMerge2.merge 復(fù)制代碼
    4.4.4 主要代碼分析

    MergeManifests 也是繼承了 IncrementalTask,但是沒有實現(xiàn) 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()); // ManifestMerger2.performSystemPropertiesInjection 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);}} } 復(fù)制代碼
  • 合并 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 定義重復(fù)的話,會輸出下面信息,我們平時應(yīng)該或多或少見過類似的錯誤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();} } 復(fù)制代碼
  • 合并依賴庫的 manifest
  • for (LoadedManifestInfo libraryDocument : loadedLibraryDocuments) {mLogger.verbose("Merging library manifest " + libraryDocument.getLocation());xmlDocumentOptional = merge(xmlDocumentOptional, libraryDocument, mergingReportBuilder);if (!xmlDocumentOptional.isPresent()) {return mergingReportBuilder.build();} } 復(fù)制代碼
  • 處理 manifest 的 placeholders
  • performPlaceHolderSubstitution(loadedMainManifestInfo, xmlDocumentOptional.get(), mergingReportBuilder, severity); 復(fù)制代碼
  • 之后對最終合并后的 manifest 中的一些屬性重新進行一次替換,類似步驟 4
  • 保存 manifest 到 build/intermediates/manifest/fullxxx/AndroidManifest.xml 這就生成了最終的 Manifest 文件
  • 4.5 transformClassesWithDexBuilderForDebug

    4.5.1 實現(xiàn)類

    DexArchiveBuilderTransform

    4.5.2 整體實現(xiàn)圖

    4.5.3 調(diào)用鏈路
    DexArchiveBuilderTransform.transform -> DexArchiveBuilderTransform.convertJarToDexArchive -> DexArchiveBuilderTransform.convertToDexArchive -> DexArchiveBuilderTransform.launchProcessing -> DxDexArchiveBuilder.convert 復(fù)制代碼
    4.5.4 主要代碼分析

    在 DexArchiveBuilderTransform 中,對 class 的處理分為兩種方式,一種是對 目錄下的 class 進行處理,一種是對 .jar 里的 class 進行處理。
    為什么要分為這兩種方式呢?.jar 中的 class 一般來說都是依賴庫,基本上不會改變,gradle 在這里做了一個緩存,但是兩種方式最終都會調(diào)用到 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) {// 如果沒有緩存,調(diào)用 convertToDexArchive 去生成 dexreturn convertToDexArchive(context, toConvert, transformOutputProvider, false);} else {// 如果有緩存,直接使用緩存的 jarFile outputFile = getPreDexJar(transformOutputProvider, toConvert, null);Files.copy(cachedVersion.toPath(),outputFile.toPath(),StandardCopyOption.REPLACE_EXISTING);// no need to try to cache an already cached version.return ImmutableList.of();}} 復(fù)制代碼
    • convertToDexArchive 處理 dir 以及 jar 的后續(xù)處理
      對 dir 處理使用 convertToDexArchive
      其中會調(diào)用 launchProcessing
    private static void launchProcessing(@NonNull DexConversionParameters dexConversionParameters,@NonNull OutputStream outStream,@NonNull OutputStream errStream)throws IOException, URISyntaxException {// ...boolean hasIncrementalInfo =dexConversionParameters.isDirectoryBased() && dexConversionParameters.isIncremental;// 判斷 class 是否新增或者修改過,如果新增或者修改過,就需要處理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)) {// 內(nèi)部調(diào)用 dx 或者 d8 去打 dexdexArchiveBuilder.convert(input.entries(bucketFilter),Paths.get(new URI(dexConversionParameters.output)),dexConversionParameters.isDirectoryBased());} catch (DexArchiveBuilderException ex) {throw new DexArchiveBuilderException("Failed to process " + inputPath.toString(), ex);}} 復(fù)制代碼

    在 launchProcessing 中,有下面幾個步驟:

  • 判斷目錄下的 class 是否新增或者修改過
  • 調(diào)用 DexArchiveBuilder.build 去處理修改過的 class
  • DexArchiveBuilder 有兩個子類,D8DexArchiveBuilder 和 DxDexArchiveBuilder,分別是調(diào)用 d8 和 dx 去打 dex
  • 4.6 transformDexArchiveWithExternalLibsDexMergerForDebug

    4.6.1 實現(xiàn)類

    ExternalLibsMergerTransform

    4.6.2 整體實現(xiàn)圖

    4.6.3 調(diào)用鏈路

    這一步是處理依賴庫的 dex,把上一步生成的依賴庫的 dex merge 成一個 dex

    // dx ExternalLibsMergerTransform.transform -> DexMergerTransformCallable.call -> DxDexArchiveMerger.mergeDexArchives -> DxDexArchiveMerger.mergeMonoDex -> DexArchiveMergerCallable.call -> DexMerger.merge 復(fù)制代碼// d8 ExternalLibsMergerTransform.transform -> DexMergerTransformCallable.call -> D8DexArchiveMerger.mergeDexArchives -> 調(diào)用 D8 命令 復(fù)制代碼

    這里邏輯比較簡單,就不具體分析了

    4.7 transformDexArchiveWithDexMergerForDebug

    4.7.1 實現(xiàn)類

    DexMergerTransform

    4.7.2 整體實現(xiàn)圖

    4.7.3 調(diào)用鏈路

    和上一步類似

    // dx DexMergerTransform.transform -> DexMergerTransform.handleLegacyAndMonoDex -> DexMergerTransformCallable.call -> DxDexArchiveMerger.mergeDexArchives -> DxDexArchiveMerger.mergeMonoDex -> DexArchiveMergerCallable.call -> DexMerger.merge 復(fù)制代碼// d8 DexMergerTransform.transform -> DexMergerTransform.handleLegacyAndMonoDex -> DexMergerTransformCallable.call -> D8DexArchiveMerger.mergeDexArchives -> 調(diào)用 D8 命令 復(fù)制代碼

    五、本文重點

  • Android Gradle Plugin 中各個 Task 的作用及實現(xiàn)類,具體可參考文中第二節(jié)「Task 對應(yīng)實現(xiàn)類」
  • 如何閱讀 Task 的代
  • 【Android 修煉手冊】系列內(nèi)容 每周更新
    歡迎關(guān)注下面賬號,獲取更新:
    微信搜索公眾號: ZYLAB
    Github
    掘金

    總結(jié)

    以上是生活随笔為你收集整理的【Android 修炼手册】Gradle 篇 -- Android Gradle Plugin 主要 Task 分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 影音先锋中文字幕在线视频 | 久久久精品免费观看 | 国产黄色观看 | 国产视频二区三区 | 国产午夜精品一区二区三区欧美 | 亚洲欧美国产高清 | 中文字幕第23页 | 秋霞精品一区二区三区 | 午夜影院在线观看18 | 巨茎大战刘亦菲 | 美女一区二区三区视频 | 亚洲欧洲国产精品 | 亚洲区一区二区三区 | 欧美黄片一区 | 精品人妻一区二区三区日产乱码卜 | 91成人免费在线观看 | 国产午夜一级 | www.久久久久 | 国产福利第一页 | 中文字幕免费在线观看视频 | 国产二区av| 少妇网站在线观看 | 亚洲综合自拍偷拍 | a∨色狠狠一区二区三区 | 中文字幕永久视频 | 国产精品久久久久久免费 | 午夜三级在线观看 | 黄色成年人| 成人私密视频 | av片免费在线 | 久久mm| mm131丰满少妇人体欣赏图 | 日本美女黄色大片 | 亚洲成人a v | 久久久久看片 | 国产精品午夜久久 | 黄色片子免费看 | 毛片视频网站在线观看 | 成人少妇影院yyyy | 特大黑人娇小亚洲女 | 免费看的黄色 | 国产男女猛烈无遮挡 | 日韩无马| 免费网站www在线观看 | 中文字幕av专区dvd | 国产又粗又猛又爽又黄视频 | 亚洲天堂网在线视频 | 小宵虎南在线观看 | 真实的国产乱xxxx在线91 | 免费的黄色片 | 午夜福利理论片在线观看 | 久久久久9999| 青青草伊人网 | 综合激情久久 | 黄色片hd | 国产女人被狂躁到高潮小说 | 亚洲欧洲精品在线 | 插插综合视频 | aa一级片 | 澳门av在线 | 亚洲成熟毛多妇女av毛片 | 久久综合精品国产二区无码不卡 | 婷婷婷色 | 亚洲av无码成人精品区 | 国产欧美一区二区精品性色超碰 | 三级三级久久三级久久18 | 欧洲精品码一区二区三区免费看 | 亚洲欧洲日本一区二区三区 | 床上激情网站 | 女生喷液视频 | 黄色你懂的 | 136福利视频导航 | 91天天操 | 亚州欧美在线 | 亚洲视频免费观看 | 国产又大又黄的视频 | 欧美用舌头去添高潮 | 深夜精品福利 | 亚洲男人天堂2024 | 91在线公开视频 | 久久精品免费看 | 国产免费不卡 | 亚洲午夜网站 | 亚洲黄色天堂 | 色亚洲视频 | 1级黄色大片 | 中文字幕1区2区 | 亚洲啪视频 | 欧美日韩色视频 | 日本一本在线观看 | 日韩一区二区免费在线观看 | 国产高清免费在线 | 一区在线免费 | 亚洲熟悉妇女xxx妇女av | 一本色道久久88 | 日韩欧美在线观看一区二区三区 | 一区二区三区精品在线 | 欧美射| 91精品综合久久久久久 |