每次新建项目,我们都会生成build.gradle,如果是app模块则会引入:
apply plugin: 'com.android.application'
如果是lib:
apply plugin: 'com.android.library'
问题来了:
- apply plugin: 'com.android.application'背后的原理是?
更多问答 >>
-
2020-08-23 23:54
-
2020-08-26 21:11
-
2020-09-09 23:54
-
2020-10-03 11:43
-
每日一问 | 启动了Activity 的 app 至少有几个线程?
2020-10-12 00:47 -
每日一问| View 绘制的一个细节,如何修改 View 绘制的顺序?
2020-08-12 10:21 -
每日一问 | 比 removeView 更轻量的操作,你了解过吗?
2020-07-27 01:14 -
每日一问 | RecyclerView的多级缓存机制,每级缓存到底起到什么样的作用?
2020-07-19 23:56 -
2020-07-08 23:05
-
每日一问 | Android P 上,需要配置 network_security_config ,才能抓包,正确吗?
2020-06-29 21:26
这个问题可能问的地方有两个:apply plugin: 'com.android.application'
背后的原理是?字符串
com.android.application
所对应的Plugin类在apply
时具体做了哪些操作?这个
apply
方法是如何通过后面的字符串找到对应的Plugin类的呢?先来看第二问:
学习过Gradle相关知识的同学会知道,apply plugin: 'com.android.application
这句代码其实就是把一个Map(key="plugin",value="com.android.application")对象传进ProjectScript的apply(Map options)
方法里而已。然后这个Map对象的value(现在是"com.android.application"
这个字符串),会作为PluginId传到DefaultPluginManager的apply
方法中,在这里,DefaultPluginManager又会通过DefaultPluginRegistry的lookup
方法来初始化一个PluginImplementation对象,这个对象间接携带了前面说到的Map对象的value所对应的Plugin类,接着,这个Plugin类会在DefaultPluginManager的addPlugin
方法中通过反射实例化,实例化了之后,最终会调用它的apply
方法,并把Project对象传进去(注意!现在这个的apply
方法,就是第一问所讲的apply
方法,所以要回答第一问,可以从这里开始看起)。重点来了:DefaultPluginRegistry的lookup
方法是怎么知道"com.android.application"
对应的是哪个Plugin类呢?其实是这样的,它会先获取成员变量classLoaderScope
所持有的ClassLoader实例,然后通过这个ClassLoader的getResource
方法来查找META-INF/gradle-plugins/
目录下的com.android.application.properties
文件!(当然了,如果是apply plugin: 'com.android.library'
那就是查找com.android.library.properties
文件)如果有找到的话,会取出里面的配置信息:implementation-class=插件包名.插件类名
,玩过自定义Gradle插件的同学应该对这个不陌生,这里所指定的类,就是自己实现的那个Plugin类。打开gradle路径/caches/modules-2/files-2.1/com.android.tools.build/gradle/gradle版本号/xxxx
文件夹下的jar包,并定位到META-INF/gradle-plugins/
目录下会看到很多properties文件,上面说的com.android.application.properties
也在其中,打开一看,你会发现implementation-class
指向的是com.android.build.gradle.AppPlugin
,所以com.android.application
对应的Plugin类就是这个AppPlugin了,也就是apply plugin: 'com.android.application
这行代码,最终会进入到AppPlugin的apply
方法中。小结:
build.gradle文件中的apply plugin: 'xxx'
,其实就是查找各个jar包中META-INF/gradle-plugins/目录下的xxx.properties
文件,找到之后,会取出其implementation-class
属性值并通过反射实例化这个属性值所指向的类对象,然后调用其apply
方法。源码分析:
来看看apply plugin: 'com.android.application
这句代码被执行时发生了什么。其实我们在研究像这种找不到源头,还有异步任务满天飞的代码,可以通过Debug来帮助我们快速了解具体执行流程。不过,如果你直接在AS的Gradle窗口中,随便右键个Task然后选Debug:apply
方法时,会发现根本定位不到相关代码,但Debug窗口是正常的:core
和core-api
,并分别把它们里面的org
文件夹复制到你的测试项目java
目录下。再次启动Debug:版本号需要跟项目中所使用的版本一致。
好,现在开始源码分析:
从
apply plugin: 'com.android.application
断点进去,会来到ProjectScript的apply
方法:这里的
接着就是它的getScriptTarget
会返回DefaultProject对象,继承自抽象类AbstractPluginAware。apply
方法啦:可以看到创建了一个DefaultObjectConfigurationAction对象,并调用了它的
execute
方法:来到这里,会遍历一个装着Runnable的Set,并逐个调用
这些Runnable在哪里实现呢,继续F7:run
方法。会发现原来是在
接着会逐个判断plugin
方法被调用时实现的匿名对象,里面会调用applyType
方法,并将pluginId
传了进去(这个pluginId
就是字符串"com.android.application"
)。target
是否PluginAware的实现类,如果是的话,会调用PluginManager的apply
方法,并将pluginId
传进去。继续F7会发现getPluginManager
方法返回的对象就是DefaultPluginManager:重点来了,在DefaultPluginManager的
调用apply
方法里面,会通过pluginRegistry
的lookup
方法获取PluginImplementation对象,这个对象储存的相关信息,我们前天也讲过。lookup
方法之前,还会先通过DefaultPluginId的unvalidated
方法创建一个DefaultPluginId对象:这个DefaultPluginId类代码很少,一目了然。
我们接着看lookup
方法:可以看出来这是一个递归方法,如果
那它是怎么找呢?看中间的那个parent
(父插件注册表)不为空的话,会优先通过parent
的lookup
方法来查找PluginImplementation对象,parent
找不到才自己找。lookup
方法,无论pluginId.getNamespace()
是不是null,都会调用uncheckedGet
方法,把idMappings
和一个PluginIdLookupCacheKey对象传进去。这个uncheckedGet
方法很简单,只是直接调用传进来的idMappings
的get
方法。idMappings
是LoadingCache的实例,看名字就知道这是一个缓存对象的工具类。在使用它之前,需要告诉它怎么去加载对象,看DefaultPluginRegistry的构造方法:看到了吧,
它依次从参数idMappings
是通过CacheBuilder来创建的,在创建时重写了CacheLoader的load
方法,我们重点就看这个方法,因为等下idMappings
找不到对象时会调用这个方法获取新实例。key
中获取了pluginId
和classLoader
实例,然后创建了一个ClassloaderBackedPluginDescriptorLocator对象,把classLoader
传了进去;接着又调用了它的findPluginDescriptor
方法,把pluginId
的String传了进去。现在请看回上面的DefaultPluginId源码,它重写了toString
,直接return了value
,这个value
就是"com.android.application"
字符串。好,现在来看下它的findPluginDescriptor
方法:!!对吧!就像前天说的那样,是通过ClassLoader来查找
如果从这个ClassLoader中找到了,就会创建一个PluginDescriptor对象并返回。那现在看回刚刚的META-INF/gradle-plugins/
目录下名为pluginId.properties
的文件的。load
方法,如果返回的PluginDescriptor对象不为空,就会调用getImplementationClassName
方法:哈哈,就是前天说的那样,它会取出这个配置文件key为
接着看,取出类名之后就是implementation-class
的value(com.android.application.properties
的implementation-class
就是com.android.build.gradle.AppPlugin
)。loadClass
获取目标插件的Class对象。最后是创建对应的PluginImplementation对象并返回。好了,现在的lookup
方法已经分析完毕,是时候返回到上面的DefaultPluginManager了,在DefaultPluginManager的apply
方法中,通过lookup
方法获取到PluginImplementation对象后会调用doApply
方法:这里看似是异步任务,我们把目光放在下面的
然后会来到这里:execute
方法,即buildOperationExecutor.run
那句,在这里打断点后F9。在这里也直接进入
execute
方法:又是异步,断点
worker.execute(buildOperation, context);
这一行,F9:buildOperation.run(context);
,F7:到了到了,看
addPlugin
方法,它会先通过producePluginInstance
方法获取Plugin对象实例,来看一下producePluginInstance
方法里面发生了什么:emmmm,如果
好,回到上面的findInstance
方法找不到现有的实例,就会调用instantiatePlugin
方法(参数type
其实就是刚刚从com.android.application.properties
中取出来的Class对象),在这方法内会通过反射创建type
的实例!!!也就是com.android.build.gradle.AppPlugin
对象!addPlugin
方法,在获取到目标Plugin实例之后,会调用它的applyImperative
方法,看看:F7:
嘿嘿,看到了吧,在ImperativeOnlyPluginTarget的
那下一步自然就到了AppPlugin里了:applyImperative
方法里,调用了Plugin的apply
方法。它会先调用父类的
apply
:咦,父类在给
查看project
赋值之后,也apply
了一个插件,这个插件id叫com.android.internal.version-check
。com.android.internal.version-check.properties
会发现,它指向的是这个类:跟它的名字一样,它
好,现在回到刚刚的AppPlugin方法,发现它也apply
方法里面做的事情也是检测一下Gradle版本而已。apply
了一个id为com.android.internal.application
的插件,注意这次是com.android.internal.application
,比build.gradle中的com.android.application
多了个internal
。同样地,找到同名properties文件,会发现上面写着是com.android.build.gradle.internal.plugins.AppPlugin
,这次的AppPlugin是用java实现的,它间接继承自com.android.build.gradle.internal.plugins.BasePlugin
并且没有重写apply
方法,所以当它的apply
被调用时,其实是去到了BasePlugin的apply
方法:不过从这里开始,网上也有很多相关的文章了,我就不啰嗦了,留给同学们自己探索吧。
总结:
apply plugin: 'com.android.application'
这句代码会从com.android.application.properties
中找到AppPlugin,在AppPlugin的apply
方法里会先调用父类的apply
,而父类又会加载版本检测的Plugin,最后,在AppPlugin中还会调用到com.android.build.gradle.internal.plugins.BasePlugin
的apply
方法......emmmm,看似简单的一行代码,背后所做的工作远超出我们的预料啊。睡觉对了,建议大家一边Debug,一边看分析,这样理解更深刻。大佬NB
大佬用什么系统
目测ubuntu+仿mac主题
卧槽!!!!!!!这个居然能debug!!!!!!!!!学到了学到了
是的,就是Ubuntu + card-theme-dark主题。
是啊是啊,没想到吧😎
好像只找到了org.gradle.application.properties文件,没有com.android.application.properties,是因为的版本比较老么?本地5.4.1 ...查看更多
好像只找到了org.gradle.application.properties文件,没有com.android.application.properties,是因为的版本比较老么?本地5.4.1
找错jar包了,你要找android gradle plugin的jar包,不是gradle的jar包
大佬分析得很棒,提个建议,看源码的话,可以点击去到 .class 文件然后直接 choose source 选对应版本的 src 就可以了,复制有点麻烦~
我是弟弟。不行的,你在build.gradle中Ctrl + Click根本不会跳转到对应class中,因为AS根本找不到这个类在哪
牛逼,学到了
好久没来,缘佬改名字啦。
嘻嘻嘻,元宵节快乐
根据gradle plugin的原理,每个plguin都是一个Groovy 的Plugin
跟踪了下Android Gradle Plugin 发现最后调用的就是所以原理就是调用了下AppPlugin 然后根据开发定义的AppExtension,执行特定的gradle任务
好短
次奥 短不行吗
确实 短
Android 项目是基于gradle 编译构建的。
apply plugin: 'com.android.application' 属于gradle脚本的知识。具体可以学习下gradle 简单的知识。
gradle的脚本以.gradle结尾。一个gradle文件,就是一个project,一个project包含多个task。
gradle的开发语言类似java。很容易上手。apply plugin: 'com.android.application"。 里面封装了很多的task 针对android的。可以通过gradle tasks 查看。
不仅仅android,在java web项目中。也有 apply plugin "java" 等申明。
apply plugin: 'com.android.library”是针对library的插件。具体可以自己定义一个插件试试 很easy的
你们🐮