文章目录
- 1.什么是Extensions
- 2.如何自定义Extension
- 3.问题来了——如何通过自定义Extension给Plugin传递数据
- 4.BaseAppModuleExtension和AppPlugin部分原理
- BuildTypes是如何创建并传递数据给AppPlugin的?
- AppPlugin是如何接收数据的?
- buildTypeContainer
- 流程总结
- 5. 回归初心——扩展是如何传递数据给插件的呢?
- 6 应用
系列: Gradle系列(2)——如何自定以Gradle 插件
在上一篇Gradle系列(2)——如何自定以Gradle 插件 中,我们了解了什么是 Gradle Plugin
以及如何用Gradle Plugin
在项目构建中实现更多强力而有趣的功能。尽管在demo中我们已经实现了在整个公司的不同团队通过本地json文件实现平台化的配置,但是仍然存在某个项目需要个性化的配置,或者在构建过程中需要一些灵活便捷的自定义的构建,那么上一篇实现的功能就不能满足我们的需求,于是本篇就引入了如何自定义Gradle Extensions
1.什么是Extensions
- 我们其实经常接触Gradle Extension
Gradle Extensions 是Gradle Plugin 的一种扩展机制,通过实现自定义的Extension,我们可以在Gradle 脚本(例如build.gradle)中增加自己的配置,例如android(其实例为com.android.build.gradle.internal.dsl.BaseAppModuleExtension
或com.android.build.gradle.LibraryExtension
),
Gradle在configuration
阶段可以读取这些配置里面的内容。
2.如何自定义Extension
关于这点,百度搜索有许多好多大量海量千篇一律毫无区别的帖子告诉你怎么定义一个自定义扩展并注册,我不再展开,就按照他们写的简单写个小demo
- 定义一个扩展
public class TestExtension {public String anyString;
}
- 在Plugin实现类注册这个扩展
public class RocketPlugin implements Plugin<Project> {@Overridepublic void apply(Project project) {super.apply(project);project.getExtensions().create("anyTest", TestExtension.class);}
}
我们给build.gradle内注册了一个TestExtension的实例,名字为anyTest
- 使用这个扩展
在app/library build.gradle内
plugins {id("com.android.application")id("gradle-rocket")
}anyTest{topic = "extension test"
}
- 获取插件的配置信息
在Plugin实现类内:
public class RocketPlugin implements Plugin<Project> {@Overridepublic void apply(Project project) {super.apply(project);TestExtension anyTest = project.getExtensions().create("anyTest", TestExtension.class);project.afterEvaluate(new Action<Project>() {@Overridepublic void execute(Project project) {System.out.println(" afterEvaluate :anyTest:"+anyTest.topic);}});}
}
然后同步就可以看到输出:
!
按照大家的说法,到这一步我们实现了一个简单的扩展。
3.问题来了——如何通过自定义Extension给Plugin传递数据
到上一节,我们实现了一个扩展,简单说明如何定义注册使用扩展,但,这只是一个简陋的扩展,甚至不能称之为扩展,我称之为扩展都感觉十分脸红惭愧甚至觉得丢人。
回想看过的十数篇帖子,不由得内心怒骂好几次:
这TM是扩展?!!这TM是扩展?!!这TM是扩展?!!
你们这些灌水的人给我翻译翻译,什么TM的叫TM的扩展?!!
为什么这个东西不能叫扩展?
我们回到开头的“什么是Extensions”,扩展的核心诉求是在gradle构建脚本(比如build.gradle)中增加我们自己的配置应用到我们的自定义gradle plugin中。而在以上篇章我们的扩展在哪里应用了,可以看到一行代码:
estExtension anyTest = project.getExtensions().create("anyTest", TestExtension.class);project.afterEvaluate(new Action<Project>() {@Overridepublic void execute(Project project) {System.out.println(" afterEvaluate :anyTest:"+anyTest.topic);}});//外部直接打印System.out.println(" afterEvaluate :anyTest:"+anyTest.topic);
我们注意project.afterEvaluate
这个方法,在我的上一篇Gradle系列(2)——如何自定以Gradle 插件 中我简单讲了gradle的生命周期以及监听,afterEvaluate
表示,gradle 的configuration
阶段已经完成了。而上述代码表示,在项目配置完成后,输出一下我们扩展配置的值
。
- 实际问题
由于我们做的是平台化项目,android
和harmony
双平台打包的,两个平台应用的三方库,签名机制也不一样,所以我需要知道编译的是哪个平台,是否需要支持双平台,所以我添加了一个扩展来配置这个信息,然而我在上述afterEvaluate
生命周期去做签名的时候,发现报错(当时忘了截图,大概意思是说buildType已经创建好了,此时修改太晚了)
我们尝试在外部直接打印,你会发现,根本没有值!
那么问题来了——我们这里实现的扩展还有什么用呢?
答案是,没卵用!
你去搜索相关的帖子,千篇一律,都是这样,甚至你看官网,他们的demo也是这样!我请问一下,这样一个不能给插件传送数据,只能看没有实际用处的花架子,它叫扩展吗?
4.BaseAppModuleExtension和AppPlugin部分原理
那么到底在构建脚本怎么通过我们的扩展给插件传递数据呢?我想,我们可以看Android的AppExtension和AppPlugin是如何实现的呀。
注意:
-
com.android.application
,com.android.library
实现类分别为com.android.build.gradle.internal.plugins.AppPlugin
和com.android.build.gradle.internal.plugins.LibraryPlugin
; -
build.gradle中声明的“android”扩展的实例根据module是应用的插件不同,其实例可能是
com.android.build.gradle.internal.dsl.BaseAppModuleExtension
或com.android.build.gradle.LibraryExtension
,
BuildTypes是如何创建并传递数据给AppPlugin的?
我们以build.gradle.kts中buildTypes
为例:
plugins {id("com.android.application")id("gradle-rocket")
}
android {//。。。create("test") {isMinifyEnabled = falseisDebuggable = truesigningConfigs { }}//。。。
}
其实build.gradle的dsl写法我们可以理解为一种极致的lambda,buildTypes完整写法应该是
buildTypes {create("test", object : Action<ApplicationBuildType> {override fun execute(t: ApplicationBuildType) {t.isMinifyEnabled = falset.isDebuggable = true}})}
我们点进去这个create
方法,
可以看到,这是一个抽象类,点击左侧图标查看源码:
看得出来这个类应用还是比较广泛的,一时难以确定实现类是哪个。我们转变思路看AppPlugin
:
AppPlugin是如何接收数据的?
查看AppPlugin源码
public class AppPluginextends AbstractAppPlugin<com.android.build.api.dsl.ApplicationExtension,ApplicationAndroidComponentsExtension,ApplicationVariantBuilderImpl,ApplicationVariantImpl> {@Injectpublic AppPlugin(ToolingModelBuilderRegistry registry,SoftwareComponentFactory componentFactory,BuildEventsListenerRegistry listenerRegistry) {super(registry, componentFactory, listenerRegistry);}@NonNull@Overrideprotected BaseExtension createExtension(@NonNull DslServices dslServices,@NonNull GlobalScope globalScope,@NonNullDslContainerProvider<DefaultConfig, BuildType, ProductFlavor, SigningConfig>dslContainers,@NonNull NamedDomainObjectContainer<BaseVariantOutput> buildOutputs,@NonNull ExtraModelInfo extraModelInfo) {ApplicationExtensionImpl applicationExtension =dslServices.newDecoratedInstance(ApplicationExtensionImpl.class, dslServices, //..忽略一万行代码//创建“android”,注入到所有Extensions中return project.getExtensions().create("android",BaseAppModuleExtension.class,dslServices,globalScope,buildOutputs,dslContainers.getSourceSetManager(),extraModelInfo,applicationExtension);}}
AppPlugin
和LibraryPlugin
代码都相当简单,主要区别在各自的泛型以及createExtension
时android
的实现类不同。逻辑都在其父类com.android.build.gradle.internal.plugins.BasePlugin
,这个类也实现了Plugin
接口,我们主要看apply
方法
@Overridepublic final void apply(@NonNull Project project) {CrashReporting.runAction(() -> {basePluginApply(project);pluginSpecificApply(project);project.getPluginManager().apply(AndroidBasePlugin.class);});}
接着进入basePluginApply
private void basePluginApply(@NonNull Project project) {// We run by default in headless mode, so the JVM doesn't steal focus.System.setProperty("java.awt.headless", "true");this.project = project;//...configuratorService.recordBlock(ExecutionType.BASE_PLUGIN_PROJECT_CONFIGURE,project.getPath(),null,this::configureProject);configuratorService.recordBlock(ExecutionType.BASE_PLUGIN_PROJECT_BASE_EXTENSION_CREATION,project.getPath(),null,this::configureExtension);configuratorService.recordBlock(ExecutionType.BASE_PLUGIN_PROJECT_TASKS_CREATION,project.getPath(),null,this::createTasks);}
这3方法都比较重要,但我们要看configureExtension
:
private void configureExtension() {DslServices dslServices = globalScope.getDslServices();final NamedDomainObjectContainer<BaseVariantOutput> buildOutputs =project.container(BaseVariantOutput.class);project.getExtensions().add("buildOutputs", buildOutputs);//1.创建BuildType 工厂类variantFactory = createVariantFactory(projectServices, globalScope);variantInputModel =new LegacyVariantInputManager(dslServices,variantFactory.getVariantType(),new SourceSetManager(project,isPackagePublished(),dslServices,new DelayedActionsExecutor()));//2.子类中重载的方法 extension =createExtension(dslServices, globalScope, variantInputModel, buildOutputs, extraModelInfo);//3.把扩展添加到全局作用域 globalScope.setExtension(extension);androidComponentsExtension = createComponentExtension(dslServices, variantApiOperations);variantManager =new VariantManager(globalScope,project,projectServices.getProjectOptions(),extension,variantApiOperations,variantFactory,variantInputModel,projectServices);registerModels(registry,globalScope,variantInputModel,extension,extraModelInfo);// create default Objects, signingConfig first as its used by the BuildTypes.//创建默认的buildType和签名variantFactory.createDefaultComponents(variantInputModel);createAndroidTestUtilConfiguration();}
这段代码我们需要了解的有三处:
createVariantFactory
创建了我们的buildTypes工厂- 调用熟悉的
createExtension
创建我们的扩展实例,就是在上面提到子类中重载的方法。- 把扩展实例添加到全局作用域
- 创建默认的buildType和签名
那么我们接下来createVariantFactory
是怎么创建的,继而看我们的BuildType到底怎么创建的。
protected abstract VariantFactory<VariantBuilderT, VariantT> createVariantFactory(@NonNull ProjectServices projectServices, @NonNull GlobalScope globalScope);
这是个抽象方法,我们看实现类
能看到AppPlugin
,这个类是BaseAppModuleExtension
的父类
@NonNull@Overrideprotected ApplicationVariantFactory createVariantFactory(@NonNull ProjectServices projectServices, @NonNull GlobalScope globalScope) {return new ApplicationVariantFactory(projectServices, globalScope);}
最终到了ApplicationVariantFactory
class ApplicationVariantFactory(projectServices: ProjectServices,globalScope: GlobalScope
) : AbstractAppVariantFactory<ApplicationVariantBuilderImpl, ApplicationVariantImpl>(projectServices,globalScope
) {//。。。
}
这个类的父类是AbstractAppVariantFactory
,我们先记住这个类,返回到configureExtension
,查看variantFactory.createDefaultComponents(variantInputModel);
fun createDefaultComponents(dslContainers: DslContainerProvider<DefaultConfig, BuildType, ProductFlavor, SigningConfig>)
这是VariantFactory
接口的抽象方法,接着看实现类AbstractAppVariantFactory
override fun createDefaultComponents(dslContainers: DslContainerProvider<DefaultConfig, BuildType, ProductFlavor, SigningConfig>) {// must create signing config first so that build type 'debug' can be initialized// with the debug signing config.//创建默认的debug签名dslContainers.signingConfigContainer.create(BuilderConstants.DEBUG)//创建默认的debug和release buildTypedslContainers.buildTypeContainer.create(BuilderConstants.DEBUG)dslContainers.buildTypeContainer.create(BuilderConstants.RELEASE)}
这个方法里做了两件事:
- 创建默认的debug签名
- 创建默认的debug和releae buildType
这就解释了为什么我们项目为什么不配置签名也能安装,以及两个默认的buidType从何而来。(这段代码救了我的命,在做平台化Flavor和签名的时候要配置我们的签名密钥,创建SigningConfig遇到问题,就是看这段代码找到了解决方法)
好,我们接着往下看dslContainers.buildTypeContainer.create(BuilderConstants.DEBUG)
:
是不是回到了远点,又是NamedDomainObjectContainer
。
buildTypeContainer
那么我们的buildTypeContainer
从何而来呢?
我们要看dslContainers
;
从createDefaultComponents()
,从方法形参可以看到它的类型是DslContainerProvider
;
往回退到configureExtension()
,可以看到dslContainers
就是variantInputModel
;在configureExtension
开始的地方创建的:
variantInputModel =new LegacyVariantInputManager(dslServices,variantFactory.getVariantType(),new SourceSetManager(project,isPackagePublished(),dslServices,new DelayedActionsExecutor()));
我们再看这个LegacyVariantInputManager
的代码:
class LegacyVariantInputManager(dslServices: DslServices,variantType: VariantType,sourceSetManager: SourceSetManager
) : AbstractVariantInputManager<DefaultConfig, BuildType, ProductFlavor, SigningConfig>(dslServices,variantType,sourceSetManager
) {override val buildTypeContainer: NamedDomainObjectContainer<BuildType> =dslServices.domainObjectContainer(BuildType::class.java, BuildTypeFactory(dslServices))override val productFlavorContainer: NamedDomainObjectContainer<ProductFlavor> =dslServices.domainObjectContainer(ProductFlavor::class.java,ProductFlavorFactory(dslServices))override val signingConfigContainer: NamedDomainObjectContainer<SigningConfig> =dslServices.domainObjectContainer(SigningConfig::class.java,SigningConfigFactory(dslServices,getBuildService(dslServices.buildServiceRegistry,AndroidLocationsBuildService::class.java).get().getDefaultDebugKeystoreLocation()))override val defaultConfig: DefaultConfig = dslServices.newInstance(DefaultConfig::class.java,BuilderConstants.MAIN,dslServices)
}
这里我特意多粘贴了点代码,是不是看着很熟悉,有productFlavor
和signingConfigContainer
, defaultConfig
。
我们接着看buildTypeContainer = dslServices.domainObjectContainer
,它是接口DslServices
的方法,唯一实现类DslServicesImpl
class DslServicesImpl constructor(projectServices: ProjectServices,override val sdkComponents: Provider<SdkComponentsBuildService>
) : BaseServicesImpl(projectServices), DslServices {override fun <T> domainObjectContainer(type: Class<T>,factory: NamedDomainObjectFactory<T>): NamedDomainObjectContainer<T> =projectServices.objectFactory.domainObjectContainer(type, factory)}
继续点进去domainObjectContainer
,他是接口ObjectFactory
的方法,它的有效实现类是DefaultObjectFactory
,查看它的方法:
@Overridepublic <T> NamedDomainObjectContainer<T> domainObjectContainer(Class<T> elementType, NamedDomainObjectFactory<T> factory) {return domainObjectCollectionFactory.newNamedDomainObjectContainer(elementType, factory);}
继续点进去仍然是个接口DomainObjectCollectionFactory
:
public interface DomainObjectCollectionFactory {<T> NamedDomainObjectContainer<T> newNamedDomainObjectContainer(Class<T> elementType, NamedDomainObjectFactory<T> factory);
}
接着看它唯一实现类DefaultDomainObjectCollectionFactory
:
是不是很绕啊,确实这么绕,山高水远,道阻且长,说实话
BaePlugin
public class DefaultDomainObjectCollectionFactory implements DomainObjectCollectionFactory {@Overridepublic <T> NamedDomainObjectContainer<T> newNamedDomainObjectContainer(Class<T> elementType, NamedDomainObjectFactory<T> factory) {Instantiator instantiator = instantiatorFactory.decorateLenient();return Cast.uncheckedCast(instantiator.newInstance(FactoryNamedDomainObjectContainer.class, elementType, instantiator, new DynamicPropertyNamer(), factory, mutationGuard, collectionCallbackActionDecorator));}}
看到这里就没有必要继续往下看了,其实已经能看出来了,这里其实就是反射创建了FactoryNamedDomainObjectContainer
的实例,那我们接着看它
public class FactoryNamedDomainObjectContainer<T> extends AbstractNamedDomainObjectContainer<T> {public FactoryNamedDomainObjectContainer(Class<T> type, Instantiator instantiator, Namer<? super T> namer, NamedDomainObjectFactory<T> factory, MutationGuard crossProjectConfiguratorMutationGuard, CollectionCallbackActionDecorator collectionCallbackActionDecorator) {super(type, instantiator, namer, collectionCallbackActionDecorator);this.factory = factory;this.crossProjectConfiguratorMutationGuard = crossProjectConfiguratorMutationGuard;}@Overrideprotected T doCreate(String name) {return factory.create(name);}
}
最终,我们找到了dslContainers.buildTypeContainer
的实现类NamedDomainObjectContainer
(这里我特意贴了构造函数和一个方法,先记住,然后往后看)
它并没有实现create
方法,我们接着看它的父类
public abstract class AbstractNamedDomainObjectContainer<T> extends DefaultNamedDomainObjectSet<T> implements NamedDomainObjectContainer<T>, HasPublicType {@Overridepublic T create(String name) {assertMutable("create(String)");return create(name, Actions.doNothing());}@Overridepublic T create(String name, Closure configureClosure) {assertMutable("create(String, Closure)");return create(name, ConfigureUtil.configureUsing(configureClosure));}@Overridepublic T create(String name, Action<? super T> configureAction) throws InvalidUserDataException {assertMutable("create(String, Action)");assertCanAdd(name);//创建BuildType实例T object = doCreate(name);//添加到集合中add(object);//传递给我们的回调,让我们自己配置configureAction.execute(object);return object;}}
可以看到实现了NamedDomainObjectContainer
的3个create
方法,最主要的是第三个方法,我们再次回顾一下build.gradle
里面
buildTypes {create("test", object : Action<ApplicationBuildType> {override fun execute(t: ApplicationBuildType) {t.isMinifyEnabled = falset.isDebuggable = true}})}
对比一下上面的方法,是不是觉得柳暗花明了😎😎😎
我们知道,create
创建的就是BuildType
实例(它实现了ApplicationBuildType),这里主要做了3件事:
- 创建BuildType实例
- 添加到缓存集合中
- 传递给我们的回调,由我们自己去配置
看到希望了,我们接着往下看,我们的BuildType是怎么创建出来的,doCreate
是个抽象方法,FactoryNamedDomainObjectContainer
:
@Overrideprotected T doCreate(String name) {return factory.create(name);}
看到这个方法,我震惊了🙃🙃🙃我费尽心思追了这么久想看factory是什么终于找到了FactoryNamedDomainObjectContainer
,你告诉我创建BuildType的还不是它?🤦♂️可是我能有什么办法啊,为了维护世界和平,为了沉没成本,只能继续看啊!
那我们看factory
是哪里来的,记得我说前面特意贴的构造函数吗,竟然是构造函数传进来的,那我就返回往前看,发现我们来时的路上每一个方法的参数都是它的身影,直到LegacyVariantInputManager
:
override val buildTypeContainer: NamedDomainObjectContainer<BuildType> =dslServices.domainObjectContainer(BuildType::class.java, BuildTypeFactory(dslServices))
看它的第二个参数点进去:
public class BuildTypeFactory implements NamedDomainObjectFactory<BuildType> {@NonNull private DslServices dslServices;public BuildTypeFactory(@NonNull DslServices dslServices) {this.dslServices = dslServices;}@NonNull@Overridepublic BuildType create(@NonNull String name) {return dslServices.newInstance(BuildType.class, name, dslServices);}
}
诺,就是这里了,后面我实在不想浪费时间继续贴了,相信大家已经看腻了,总结六个字——反射创建实例
流程总结
ok,到这一步,我们就明白了:
BuildType
是由FactoryNamedDomainObjectContainer
创建的;- 实例是由
BuildTypeFactory
反射创建的;BuildType
实例创建后添加到了FactoryNamedDomainObjectContainer
,然后又给到我们的回调由我们自己配置
AppPlugin创建默认BuildType和我们自己在build.gradle 添加方式是一样的。
5. 回归初心——扩展是如何传递数据给插件的呢?
我们回溯上篇章的内容,找到各个类的从属关系:AppExtension持有ApplicationVariantFactory,后者在前者实例化时通过构造函数注入,它又持有DslServiceImpl… :
- AppExtension -> ApplicationVariantFactory ->DslServiceImpl->FactoryNamedDomainObjectContainer->BuildType,
- 而 ApplicationVariantFactory 和 DslServiceImpl 是BasePlugin的成员变量
简单的结构图:
总结:
所以我们可以直观的理解为,ApplicationVariantFactory 就是注入A品牌Extension的一个callback,build.gralde里面看似调用AppExtension的函数,实则调用AppPlugin的成员。
经过千难万险,坎坷曲折,一路不停的追踪代码,最终我们得出这样一个很简单额结论,是不是很开心呢😂
6 应用
好,既然我们已经明白了这个原理,那么我们就可以在我们插件中应用。
改造我们的扩展:
public class RocketExtensions {//多平台支持。如若支持,会默认配置多个flavorprivate RocketConfigs mExtensionContainer;public RocketExtensions(BoosterConfigs container) {mExtensionContainer = container;}public void setMultiFlavor(boolean multi) {mExtensionContainer.setMultiFlavor(multi);}
}
新增一个配置类RocketConfigs:
public class RocketConfigs {public boolean isMultiFlavor;public void setMultiFlavor(boolean multi) {System.out.println("setMultiFlavor:" + multi);this.isMultiFlavor = multi;//doSth}
}
插件类:
public class BoosterPlugin implements Plugin {@Overridepublic void apply(Project project) {super.apply(project);//初始化配置类RocketConfigs container = new BoosterConfigs();//注入扩展project.getExtensions().create("rocket", RocketExtensions.class, container);}
}
然后发布,在应用的build.gradle里使用:
plugins {id("com.android.application")id("gradle-rocket")
}rocket {setMultiFlavor(true)
}
android{//...
}
同步一下就可以看到build输出
为了看明白这块儿代码真的费尽心思,弯弯绕绕,脑袋都晕了,不过最终搞懂这些东西还是非常开心的。
好了,本篇到此结束