Gradle AGP:如何在AGP中找到諸如adb,android.jar等環(huán)境參數?

0 引言

在仿制比如qq音樂的增量編譯工具時,demo階段使用了一些硬編碼.比如要增量編譯一個改動的java文件,其classpath需要一個android.jar. 比如'Android/Sdk/platforms/android-28/android.jar'.
以及編譯結束后需要調用adb命令 push 增量編譯產物到手機, 還需要重啟app查看運行結果.
  • 收斂問題域,本次我們探討,如何優(yōu)雅的拿到android.jar, adb命令這些, 替換掉demo階段的硬編碼.

1 先說結論 (魚)

若當前project已經 "apply plugin: 'com.android.application' " (或apply plugin: 'com.android.library')了, 那么這樣拿:

// 注, 以下代碼都需要在當前project的afterEvalute之后執(zhí)行:
// 注2,以下代碼不保證執(zhí)行(因為使用調試的變量監(jiān)視器獲取,可能訪問了部分private屬性)

// 拿adb:
project.plugins.getPlugin('android').extension.adbExecutable
project.plugins.getPlugin('android').extension.globalScope.sdkComponents.adbExecutableProvider.get()


// 拿android.jar (bootclasspath):
project.plugins.getPlugin('android').extension.globalScope.sdkComponents.sdkLoadStrategy.getAndroidJar()

看下結果示例:
獲取adb:


image.png

獲取android.jar:


image.png

其本質原理是

通過project拿到 
androidPlugin(com.android.build.gradle.internal.plugins.AppPlugin / LibraryPlugin) 
或 androidExtension (com/android/build/gradle/AppExtension / LibraryExtension).
的對象實例.

這二者都持有 com.android.build.gradle.internal.scope.GlobalScope 對象實例.

進一步的, GlobalScope持有 com.android.build.gradle.internal.SdkComponents對象實例,

進一步的, 其內部的 SdkLoadingStrategy,

進一步的, 其組合了 SdkDirectLoadingStrategy 等策略.

進一步的,跟進到 SdkLocationSourceSet, 看到其加載 localProperties, environmentProperties, systemProperties
等配置.

整個鏈路下來, 基本能感受到 AGP加載配置,解析出 包括但不僅限于 android.jar /adb 等 一系列工具和依賴信息路徑的套路.

以后網上或他人提供的類似問題的答案 無論如何繞,除了groovy/kt語法糖加持顯得特殊一些, 已經沒有秘密了.

image.png
image.png
image.png

1 怎么找到的? (漁)

筆者本次是在復制"QQ音樂Android編譯提速之路"
在硬編碼驗證后,正在工程化. 增量的編譯新增改動的代碼,需要拿到當前環(huán)境的android.jar 而不能是硬編碼.
push增量編譯產物到手機, 也需要拿當前環(huán)境的adb命令,而不是硬編碼.

先找到一個切入口: AGP創(chuàng)建的javac task 是如何附加上 android.jar的呢?

調試+閱讀源碼, 跟進這個好問題.

根據對AGP源碼基本結構的了解, AGP創(chuàng)建的task都來源于 com.android.build.gradle.internal.VariantManager
和 com.android.build.gradle.internal.TaskManager
定位到這兩個源碼,尋找javac關鍵字,容易找到:


image.png

進一步的前往 com.android.build.gradle.tasks.JavaCompileCreationAction 追蹤其輸入的classpath參數.
在JavaCompileCreationAction,留意到, 真正負責javac的是 org.gradle.api.tasks.compile.JavaCompile 這個.
(ps: AGP的 XXXTask_CreationAction 有一些常規(guī)的套路,死磕一兩個后就清楚套路了, 無法是gradle的一些provider延遲加載機制,
kotlin的一些語法糖, 支持所謂gradle所謂的dsl的一些注解處理動態(tài)字節(jié)碼工具(可參考見文章:gradle 如何生成 _Decoreted 包裝類的 )

閱讀了使用 JavaCompile , 發(fā)現(xiàn)是在其基類 AbstractCompile 的classpath屬性記錄當前編譯需要依賴的classpath的.


AbstractCompile.classpath.png

另, 正向閱讀代碼 也可能發(fā)現(xiàn)設置javac classpath的方法:

image.png

上述兩點,上下夾住的代碼就是這里, 設置javac的task JavaCompile 設置 classpath的地方:
(ps: 不熟悉kt的同學 看著可能有點點費勁)


JavaCompile.configureProperties.png

調試, 下斷點, 發(fā)現(xiàn)這個 scope.bootClasspath 就是android.jar啦(包含但不僅限于, 其他的是什么就是另外一個話題了)

  • 如此 我們找到 原來javac 的classpath中的bootclasspath中的android.jar 是來源與AGP的 VariantScope.

繼續(xù)跟進 VariantScope來源 就能得到開頭給出的'魚'了.

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容