gradle 如何生成 _Decoreted 包裝類的

how and where gradle generate _Decorated classes like DefaultProject_Decorated.

0. 背景

xxxTask --> xxxxTask_Decorated ??
經常調試gradle的小伙伴, 一定有遇到 一些 以_Decorated 結尾的類.
比如 org.gradle.api.Project的實現類 org.gradle.api.internal.project.DefaultProject, 我們在下斷點調試他的時候, 看到的是
org.gradle.api.internal.project.DefaultProject_Decorated 類.
這里面有什么故事呢?

ps: _Decorated, 網上資料真的非常稀少:


image.png

一個簡略的介紹:
stackoverflow.com/.../how-to-get-projects-implementation-in-gradle

DefaultProject_Decorated

  • 大多數人是網絡信息的獲取者. 這次我們來做網絡信息的生成者:)
  • 現在我們開始調查 _Decorated 包裝類的'前世今生'

1. 從終點開始, 生成的_Decorated類的地方

通過掃描源碼關鍵字, 結合一些推理分析, 找到疑似生成X_Decorated類的地方


image.png

2> 向下跟進, gradle如何利用ASM生成類X_Decorated類的,

-- 以及想辦法dump出 X_Decorated字節碼, 觀察其的一些特殊實現.

繼續'向下' X_Decorated類如何從ASM生成 并注冊到classLoader 并實例化的

defineDecorator:81, ClassLoaderUtils (org.gradle.internal.classloader)
define:58, AsmClassGenerator (org.gradle.model.internal.asm)
define:54, AsmClassGenerator (org.gradle.model.internal.asm)
generate:1764, AsmBackedClassGenerator$ClassBuilderImpl (org.gradle.internal.instantiation.generator)
generateUnderLock:227, AbstractClassGenerator (org.gradle.internal.instantiation.generator)
lambda$new$0:119, AbstractClassGenerator (org.gradle.internal.instantiation.generator)
transform:-1, 1964650551 (org.gradle.internal.instantiation.generator.AbstractClassGenerator$$Lambda$18)
get:124, DefaultCrossBuildInMemoryCacheFactory$AbstractCrossBuildInMemoryCache (org.gradle.cache.internal)
generate:167, AbstractClassGenerator (org.gradle.internal.instantiation.generator)
generate:130, AsmBackedClassGenerator (org.gradle.internal.instantiation.generator)
image.png

參數clazzBytes即為生成的類的字節碼, 將其導出成文件, 可觀察生成類的具體實現
直接在調試器的Evaluate窗口寫如下代碼:


image.png
image.png

使用javap觀察字節碼:

$ javap -p -v /home/moasm/xx.class
image.png

如此, 我們就學會了gradle研究的一個利器: dump X_Decorated類的字節碼, 觀察gradle運行時的 _Decorated class的實現.

- 另外提一下, 如上可以看到, X_Decorated類的生成方式是:
-   使用ASM在X的基礎上補一些字節碼后生成的字節碼, 得到數組byte[] classBytes. 
-   然后通過反射調用ClassLoader.defineClass()傳入名字,字節碼byte數組等信息, 生成的運行時所需的類.

從ASM獲取字節碼數組:


image.png

通過生成類的字節碼數組等信息, 生成運行時類:


image.png
  • 小結: 如上 我們就基本搞清楚了一個 X_Decorated類是如何生成了了.

3. 向上,棧回溯: 往發起調用, 請求生成X_Decorated類的方向看

-- 以AGP的一個task的創建為例跟進 (com.android.build.gradle.internal.tasks.MergeNativeLibsTask)

下斷點


image.png

取得調用棧,精簡棧, 觀察棧:

generate:163, AbstractClassGenerator (org.gradle.internal.instantiation.generator)
generate:130, AsmBackedClassGenerator (org.gradle.internal.instantiation.generator)
transform:59, Jsr330ConstructorSelector$1 (org.gradle.internal.instantiation.generator)
transform:54, Jsr330ConstructorSelector$1 (org.gradle.internal.instantiation.generator)
get:124, DefaultCrossBuildInMemoryCacheFactory$AbstractCrossBuildInMemoryCache (org.gradle.cache.internal)
forType:54, Jsr330ConstructorSelector (org.gradle.internal.instantiation.generator)
forParams:49, Jsr330ConstructorSelector (org.gradle.internal.instantiation.generator)
doCreate:61, DependencyInjectingInstantiator (org.gradle.internal.instantiation.generator)
newInstanceWithDisplayName:50, DependencyInjectingInstantiator (org.gradle.internal.instantiation.generator)
call:90, TaskFactory$1 (org.gradle.api.internal.project.taskfactory)
call:84, TaskFactory$1 (org.gradle.api.internal.project.taskfactory)
uncheckedCall:442, GUtil (org.gradle.util)
injectIntoNewInstance:201, AbstractTask (org.gradle.api.internal)
create:84, TaskFactory (org.gradle.api.internal.project.taskfactory)
create:48, AnnotationProcessingTaskFactory (org.gradle.api.internal.project.taskfactory)
createTask:326, DefaultTaskContainer (org.gradle.api.internal.tasks)
access$200:77, DefaultTaskContainer (org.gradle.api.internal.tasks)
createDomainObject:701, DefaultTaskContainer$TaskCreatingProvider (org.gradle.api.internal.tasks)
createDomainObject:658, DefaultTaskContainer$TaskCreatingProvider (org.gradle.api.internal.tasks)
tryCreate:941, DefaultNamedDomainObjectCollection$AbstractDomainObjectCreatingProvider (org.gradle.api.internal)
access$1401:658, DefaultTaskContainer$TaskCreatingProvider (org.gradle.api.internal.tasks)
run:684, DefaultTaskContainer$TaskCreatingProvider$1 (org.gradle.api.internal.tasks)
...

tryCreate:680, DefaultTaskContainer$TaskCreatingProvider (org.gradle.api.internal.tasks)
...
addLaterInternal:765, DefaultTaskContainer (org.gradle.api.internal.tasks)
access$900:77, DefaultTaskContainer (org.gradle.api.internal.tasks)
call:420, DefaultTaskContainer$3 (org.gradle.api.internal.tasks)
call:407, DefaultTaskContainer$3 (org.gradle.api.internal.tasks)
...
registerTask:407, DefaultTaskContainer (org.gradle.api.internal.tasks)
register:379, DefaultTaskContainer (org.gradle.api.internal.tasks)
// -------------↑ gradle如何創建task ↑ --------------------------------------------------------------------------

registerTask:37, TaskFactoryUtils (com.android.build.gradle.internal.tasks.factory)
register:45, TaskFactoryImpl (com.android.build.gradle.internal.tasks.factory)
createMergeJniLibFoldersTasks:956, TaskManager (com.android.build.gradle.internal)
createTasksForVariantScope:187, ApplicationTaskManager (com.android.build.gradle.internal)
createTasksForVariant:331, VariantManager (com.android.build.gradle.internal)
createVariantsAndTasks:207, VariantManager (com.android.build.gradle.internal)
createAndroidTasks:671, BasePlugin (com.android.build.gradle.internal.plugins)
call:-1, 1828560501 (com.android.build.gradle.internal.plugins.BasePlugin$$Lambda$552)
record:82, ThreadRecorder (com.android.builder.profile)
lambda$createTasks$4:582, BasePlugin (com.android.build.gradle.internal.plugins)
accept:-1, 1056623483 (com.android.build.gradle.internal.plugins.BasePlugin$$Lambda$546)
execute:37, CrashReporting$afterEvaluate$1 (com.android.build.gradle.internal.crash)
execute:-1, CrashReporting$afterEvaluate$1 (com.android.build.gradle.internal.crash)
// -------------↑ android AGP插件開始 ↑ -------------------------------------------------------------------------
....
afterEvaluate:-1, $Proxy36 (com.sun.proxy)  // *********某個Project的afterEvaluate()回調
...
call:94, DefaultBuildOperationExecutor (org.gradle.internal.operations)
...
execute:75, ForwardClientInput (org.gradle.launcher.daemon.server.exec)
...
proceed:104, DaemonCommandExecution (org.gradle.launcher.daemon.server.api)
run:52, StartBuildOrRespondWithBusy$1 (org.gradle.launcher.daemon.server.exec)
run:297, DaemonStateCoordinator$1 (org.gradle.launcher.daemon.server)
onExecute:64, ExecutorPolicy$CatchAndRecordFailures (org.gradle.internal.concurrent)
run:48, ManagedExecutorImpl$1 (org.gradle.internal.concurrent)
runWorker:1149, ThreadPoolExecutor (java.util.concurrent)
run:624, ThreadPoolExecutor$Worker (java.util.concurrent)
run:56, ThreadFactoryImpl$ManagedThreadRunnable (org.gradle.internal.concurrent)
run:748, Thread (java.lang)
  • 畫外音: 如上精簡棧, 容易理解AGP的task創建流程.
    經常會有這樣, 從終點棧回溯研究一個流程, 比正向閱讀代碼跟進流程容易的多. 尤其在gradle這種大量設計模式應用的地方, 一些地方, 靜態閱讀很難說清楚走向.
簡單來說, AGP創建一個XXTask 是通過事先已經從Project拿到的taskContiner去創建. 
  而taskContainer將創建task的工作交給TaskFactor.
    TaskFactory會調用到AsmBackedClassGenerator 將實際生成的類變成XXTask_Decorated
       (從TaskFactory 到 AsmBackedClassGenerator 這段不管是動態調試 還是靜態閱讀代碼 
         都是很難看清流程的, 通過這么一次棧回溯 不僅突破當前問題, 并且可以學習gradle的
        一些封裝套路, 為以后更好更快研究gradle打下基礎)

      如上我說的很'輕巧' 但若不親身實踐下 看了也幾乎沒有什么意義  -- 除非帶著跟我一樣的問題 直接拿走答案. 

4. dump X_Decorated類的字節碼, 有什么用呢?

  • 比如我們看gradle源碼 DefaultProject 有的地方寫了"Decoration takes care of the implementation"
    大意思就是 這個方法在裝飾類實現.


    image.png

此時我們就能使用上面第二節提到的辦法, 拿到 DefaultProject_Decorated 的字節碼, 直接觀察getProviders的實現


image.png

剩下的問題, 就是繼續看grade源碼去研究該研究的問題了.
如此我們就成功越過一個 _Decorated 關于的, 阻礙我們研究gradle的障礙點了.

小結, 研究gradle的裝飾類 X_Decorated 的相關流程, 我們不僅了解了問題本身,
并且使用的 '站在棧終點回溯' 等一些研究方法, 也是重要經驗.

5. 實際案例

研究 _Decorated 類的實現, 只是我們調查/解決問題, 必要路徑中的一步.
在接管AGP的 class翻譯dex后, 我們遇到了只在部分windows電腦上游的 MergeNativeLibsTask 耗時過長問題.
通過我們的自有日志可以觀察到:


image.png

該例, 構建耗時263秒. 其中mergeXXXNativeLibs 耗時198秒.
雖然不科學 但是估算范圍基本是準的: 這個一行代碼都沒改的增量編譯 因為 mergeXXXNativeLibs 而拖慢了月200秒.
即若修復他的問題, 則此次熱編譯耗時應該在60秒左右.

前面我們做了AGP的class翻譯dex的接管. dex翻譯的task參數相對來說, 清晰一些, 所以我們實現起來不難.
但是 mergeXXXNativeLibs 的輸入參數稍復雜一些. 既為了避免硬編碼, 也為了只是調查清楚問題點, 并做出優化,
我們都需要調查清楚 mergeXXXNativeLibs task的創建, 運行, inputs參數填充等流程.
有得深入, 方能淺出.

 // TODO ....待續....
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容