APTTransform实现多模块应用Application
前言
组件化开发过程中,各个模块内往往需要感知Application的生命周期,以获取context完成初始化工作等,业务上模块间或存在初始化顺序要求,比较常规的做法有:
在common模块定义AppLifecycleCallbackinterfaceAppLifecycleCallback{fungetPriority():Int0funonCreate(context:Context)}复制代码
各个模块实现AppLifecycleCallback,如Home模块classHomeAppLifecycle:AppLifecycleCallback{overridefungetPriority():Int0overridefunonCreate(context:Context){todo}}复制代码
然后在MainApplication中实现生命周期分发classMainApplication:Application(){privatevalcallbacksmutableListOf()overridefunonCreate(){super。onCreate()callbacks。add(HomeAppLifecycle())callbacks。add(UserAppLifecycle())addwhateveryouwant排序实现模块顺序分发callbacks。sortBy{it。getPriority()}callbacks。forEach{it。onCreate(this)}}}复制代码
这样能实现需求,但每增加一个模块,就得回到MainApplication中添加一个,不够优雅不够装,这时候就可以用上APTTransform实现原理各模块创建AppLifecycleCallback实现类,添加注解,apt为其生成代理类,以Proxy结尾构造AppLifecycleManager对外API,统一管理Proxycallbacksgradletransform遍历。class文件,找到Proxy的所有类,保存类路径,使用asm将代理类加入到AppLifecycleManager中
此方案主要是为了解学习apt,gradletransform,实际上有更好的实现方案。各位大佬畅所欲言,提出此方案的短板实现过程一、AppLifecycleAPI
创建androidlibrary,定义AppLifecycleCallback、AppLifecycleManager
interfaceAppLifecycleCallback{fungetPriority():Int0funonCreate(context:Context)}复制代码
AppLifecycleManager中onCreate是对外的API,在MainAppkication中调用;registerAppLifecycleCallback则是在transform阶段使用asm在AppLifecycleManager的构造方法中调用,将代理类路径传入,最终通过反射存储在callbacks中objectAppLifecycleManager{privatevarcallbacks:MutableList?nullfunonCreate(context:Context){callbacks?。run{sortBy{it。getPriority()}forEach{it。onCreate(context)}}}privatefunregisterAppLifecycleCallback(name:String){try{if(callbacksnull){callbacksmutableListOf()}valinstanceClass。forName(name)。getConstructor()。newInstance()if(instanceisAppLifecycleCallback!callbacks!!。contains(instance)){callbacks!!。add(instance)}}catch(e:Exception){e。printStackTrace()}}}复制代码二、APT创建kotlinlibrary,定义annotation
Target(AnnotationTarget。CLASS)Retention(AnnotationRetention。SOURCE)annotationclassAppLifecycle()复制代码创建kotlinlibrary,定义Processor,获取所有AppLifecycle注解类,使用KotlinPoet生成Proxy类
AutoService(Processor::class)classAppLifecycleProcessor:AbstractProcessor(){省略部分代码overridefunprocess(p0:MutableSetoutTypeElement?,environment:RoundEnvironment?):Boolean{environment?。run{getElementsAnnotatedWith(AppLifecycle::class。java)。filter{it。kindElementKind。CLASS}。filter{(itasTypeElement)。interfaces。contains(elements。getTypeElement(callbackName)。asType())}。forEach{AppLifecycleProxyBuilder(itasTypeElement,elements)。build()。writeTo(filer)}}returntrue}}复制代码
KotlinPoet生成Proxy类classAppLifecycleProxyBuilder(privatevaltypeElement:TypeElement,elements:Elements){省略部分代码funbuild():FileSpec{returnFileSpec。builder(packageName,fileName)。addType(getTypeSpec())。build()}privatefungetTypeSpec():TypeSpec{returnTypeSpec。classBuilder(fileName)。addProperty(getProperty())。addSuperinterface(superInterface)。addFunction(getOnCreate())。addFunction(getPriority())。build()}privatefungetProperty():PropertySpec{对应注解类实例returnPropertySpec。builder(callback,typeElement。asClassName(),KModifier。PRIVATE)。initializer(T(),typeElement。asType())。build()}privatefungetOnCreate():FunSpec{onCreate(context:Context)returnFunSpec。builder(onCreate)。addModifiers(KModifier。OVERRIDE)。addParameter(context,contextType)。addStatement(callback。onCreate(context))。build()}privatefungetPriority():FunSpec{getPriority():IntreturnFunSpec。builder(getPriority)。addModifiers(KModifier。OVERRIDE)。returns(Int::class)。addStatement(returncallback。getPriority())。build()}}复制代码
注:kotlin使用apt,要在build。gradle要有以下两个声明plugins{idkotlinkapt}dependencies{kapt(project(:processor))}复制代码三、GradleTransform
创建kotlinlibrary
定义AppLifecyclePlugin,继承Transform,实现Project接口classAppLifecyclePlugin:Transform(),PluginProject{privatevalappLifecycleClassNamesmutableListOfString()privatevarappLifecyclesJar:File?nulloverridefuntransform(transformInvocation:TransformInvocation?){transform中遍历。class文件,类名以Proxy结尾并且实现AppLifecycleCallback。。。if(name。endsWith(proxySuffix)classReader。interfaces。contains(callbackInfo)){appLifecycleClassNames。add(name)}。。。定位到包含ApplifecycleManager的JarEntryif(jarEntity。namemanagerClassFile){appLifecyclesJaroutputJar}最终使用ClassVistor将所有Proxy类加入到Manager中的callbacks里valcv:ClassVisitorAppLifecycleVisitor(classWriter,appLifecycleClassNames)classReader。accept(cv,ClassReader。EXPANDFRAMES)classWriter。toByteArray()}}复制代码
AppLifecycleVisitorclassAppLifecycleVisitor(classVisitor:ClassVisitor,privatevalcallbacks:ListString):ClassVisitor(Opcodes。ASM9,classVisitor){overridefunvisitMethod(access:Int,name:String?,descriptor:String?,signature:String?,exceptions:ArrayoutString?):MethodVisitor{varvisitorsuper。visitMethod(access,name,descriptor,signature,exceptions)if(initname()VdescriptoraccessandOpcodes。ACCPRIVATE!0){visitorobject:AdviceAdapter(ASM9,visitor,access,name,descriptor){overridefunonMethodExit(opcode:Int){for(itemincallbacks){mv。visitVarInsn(ALOAD,0)mv。visitLdcInsn(item。replace(,。))mv。visitMethodInsn(INVOKESPECIAL,comlauterapplifecycleAppLifecycleManager,registerAppLifecycleCallback,(LjavalangString;)V,false)}}}}returnvisitor}}复制代码
创建完AppLifecyclePlugin,创建文件srcmainresourcesMETAINFgradlepluginslauter。applifecycle。propertiesimplementationclasscom。lauter。applifecycle。AppLifecyclePlugin复制代码
到此,工作基本完成。只需要将plugin发布到本地,就可以测试功能了四、发布测试
在上文创建的plugin项目下build。gradle中添加:plugins{。。。添加publishidmavenpublish}。。。发布到本地publishing{publications{mavenJava(MavenPublication){fromcomponents。javagroupIdio。github。chenlauterartifactIdapplifecycleversion1。0}}repositories{mavenLocal()maven{url。。localpluginrepository}}}复制代码
在gradle中执行publish,发布完成后项目中会新增localpluginrepository文件夹
在project的build。gradle中添加依赖buildscript{repositories{。。。添加依赖,gradle7。1之后是到setting。graldepluginManagement中添加maven{url。localpluginrepository}}dependencies{。。。classpath(io。github。chenlauter:applifecycle:1。0)}}复制代码
最后在app的build。gradle中添加plugins{。。。这里跟第三步创建的lauter。applifecycle。properties文件名对应idlauter。applifecycle}复制代码
至此,所以依赖都配置完毕,运行工程,在appbuild下,用AndroidStudio直接打开apk查看AppLifecycleManager的字节码,可以看到,在构造方法中已加入两个Proxy类路径调用:
通过查看日志打印,也能看到功能正常DAppLifecycle:HomeAppLifecycleonCreateDAppLifecycle:MainAppLifecycleonCreate复制代码项目地址
github。comChenLauter启发参考
juejin。cnpost702921
原文链接:https:juejin。cnpost7126419784471674887