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 ....待续....
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,377评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,390评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,967评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,344评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,441评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,492评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,497评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,274评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,732评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,008评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,184评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,837评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,520评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,156评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,407评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,056评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,074评论 2 352

推荐阅读更多精彩内容