Android開發升級AGP7.0後的一些適配方法技巧

升級

年初瞭,我們打算升級下apg,這樣之後就擁抱下jetpack compose瞭!!

想用comopse有兩個必選項agp7.0和kotlin版本1.5.31.

Java11配置

因為apg7.0需要把所有的module編譯環境切換到java11版本上,然而這個地方很容易出錯。

首先是命令行的配置,對於mac用戶來說,可能會寫死默認的java環境到1.8. 這個時候我們需要做的就是刪除bashprofile內的java配置。

同時最好用命令行java –version嘗試下輸出的版本是不是java11。

as的配置則相對來說比較簡單瞭。

按照這兩個配置完成之後重新同步下工程應該就行瞭。

AndroidComponentsExtension

之前在現在準備好告別Transform瞭嗎?的文章就簡單展開瞭下AndroidComponentsExtension,新版本更換瞭extensions。這次給項目升級適配之前寫的哪些插件,想瞭想用新不用久嗎,嘗試下新東西瞭。

onVariants

以前在寫android插件的時候很多時候都需要在gralde的afterEvaluate方法執行之後才能獲取到很多安卓對應的屬性。

這次在v2的api中,則提供瞭非常多不同的節點,讓我們在不同的階段做一些不同的事情。

比如說onVariants,beforeVariants,finalizeDsl這三個不同的階段,正常情況下我們選擇onVariants就足夠瞭。

Component Artifacts

demo 地址

Gralde 內的一部分核心是Task,但是要想寫好一個Task其實並沒有想象中的那麼容易。特別是一個CacheableTask,他更多的關註與他們的輸入和輸出。

構建緩存(build cache)的工作原理是:在緩存中存儲已編譯的類、測試輸出和其他構建構件,同時考慮所有的任務輸入,包括輸入文件內容、相關類路徑和任務配置。

所以在AGP 7.0中,提供瞭這部分新的api讓我們簡化對於task,輸入輸出這些參數的優化,讓我們可以更專註到我們想要變更的東西上。

比如說APk,MANIFEST,MAPPING_FILE,BUNDLE,AAR或者其他的一些編譯產物,當前agp給我們提供的也是相對來說比較少的一部分功能。

另外一點就是,我們如果想要知道一個task的輸入其實如果不去閱讀源碼,之後獲取對應的路徑或者源代碼,其實是一個非常繁瑣的過程。之後還要通過變更dependon或者finalizedBy等等手段將任務插入到編譯流程內。

所以就有瞭我們這次的其中一個主角Artifacts,他主要負責幫助我們將我們的task,插入到編譯流程內,讓我們盡量少的關註到輸入和輸出。

          // 生成TaskProvider 
          val taskProvider = project.tasks.register(
                  "manifestCopy${variant.name}Task",
                  ManifestSampleTask::class.java
          )
          // 獲取variant的artifacts之後將Task轉化成我們所想要的
          variant.artifacts.use(taskProvider).wiredWithFiles(
                  ManifestSampleTask::mergedManifest,
                  ManifestSampleTask::outputManifest
          ).toTransform(SingleArtifact.MERGED_MANIFEST)

這個就是官方的一個Artifacts的簡單的使用瞭,通過變化我們可以輕松的完成一個有獲取合並後的Manifest作為輸入,之後以另外一個Manifest文件作為輸出的一個task。而且會被直接添加編譯流程內,就不需要我們關心他們的前置和後置任務是啥瞭。

registerJavaGenerateTask 沒瞭

原先在v1的api上吧,有registerJavaGenerateTask這個方法,能加一些簡單的代碼生成的操作,比如j神以前生成的R2就是通過掛載這個方法。

這次v2版本我在AndroidComponentsExtension中沒找到的對應的api,所以隻能無中生友,自己搞一個出來咯。

@Override
 public void registerJavaGeneratingTask(@NonNull Task task, @NonNull File... sourceFolders) {
     getVariantData().registerJavaGeneratingTask(task, sourceFolders);
 }
 open fun registerJavaGeneratingTask(
    task: Task,
    generatedSourceFolders: Collection<File>
) {
    @Suppress("DEPRECATION")
    taskContainer.sourceGenTask.dependsOn(task)
    val fileTrees = extraGeneratedSourceFileTrees ?: mutableListOf<ConfigurableFileTree>().also {
        extraGeneratedSourceFileTrees = it
    }
    for (f in generatedSourceFolders) {
        val fileTree = services.fileTree(f).builtBy(task)
        fileTrees.add(fileTree)
    }
    addJavaSourceFoldersToModel(generatedSourceFolders)
}

我仔細觀察瞭下registerJavaGenerateTask的源代碼,發現其中隻做瞭兩件比較簡單的事情。將Task掛載到generateVariantResources任務之後,然後將生成java類的文件夾加入到sourcetSet上去,這樣就行瞭。

sourcetSet就是javac的將java轉化成class的編譯路徑。

所以相對來說就比較簡單,我們用新的api模擬出原來的效果就差不多可以瞭,我們隻要找到掛載的任務之後,順便把代碼添加到java和kotlin的sourceset中就行瞭

fun Project.registerJavaGenerateTask(
    variant: String?,
    task: TaskProvider<out Task>,
    generatedSourceFolders: Collection<File>
) {
    if (variant.isNullOrEmpty().not()) {
        variant?.apply {
            // 因為task生成在配置階段完成之後
            afterEvaluate {
                findJavaGenerateTask(variant)?.dependsOn(task)
            }
            //  獲取最新版本sourceSet
            val application = extensions.findByType(ApplicationExtension::class.java)
            application?.sourceSets {
                findByName(variant)?.apply {
                    generatedSourceFolders.forEach {
                        java.srcDir(it)
                        kotlin.srcDir(it)
                    }
                }
            }
        }
    }
}

這次升級適配主要的代碼就是這個瞭,其實代碼量上來說不多。但是把有個坑點,之前因為偷懶就直接用瞭setSrcDirs這個api,所以文件就被覆蓋瞭。導致瞭一部分代碼沒有被編譯成class,導致瞭classnotfound異常。

其他

獲取applicationId,我們的插件內有對於應用id的判斷,之後進行不同的manifest pleaceholder的調整。邏輯比較簡單,隻是切換瞭新版本的api而已。

  if (variant is ApplicationVariant) {
    val applicationId = variant.applicationId.get()
      variant.manifestPlaceholders.put("xxxxx", applicationId)
  }

對resValue插入新的string或者values。也是原來就有的能力,但是要對新版本進行一次小小的適配和更換。

private fun addAPGClassFile(config: Variant, key: String, value: String) {
    val resValue = ResValue(value)
    val reskey = config.makeResValueKey("string", key)
    config.resValues.put(reskey, resValue)
}

啟動configuration cache

啟用配置緩存的操作,本質上是在項目的 gradle.properties 文件中設置瞭環境變量 org.gradle.unsafe.configuration-cache=true。

結尾

AGP對我們這個算是每年一更新瞭,會碰到一些新的有趣的api以及新的寫法。另外每次新版本的AGP對於編譯上都有變更和優化,更多關於AGP7.0升級適配的資料請關註WalkonNet其它相關文章!

推薦閱讀: