APT + Transform 实现多模块应用Application生命周期分发

前言

组件化开发过程中,各个模块内往往需要感知Application的生命周期,以获取context完成初始化工作等,业务上模块间或存在初始化顺序要求,比较常规的做法有:

在common模块定义 AppLifecycleCallback

interface AppLifecycleCallback {    fun getPriority(): Int = 0    fun onCreate(context: Context)}复制代码

各个模块实现 AppLifecycleCallback ,如Home模块

class HomeAppLifecycle : AppLifecycleCallback {    override fun getPriority(): Int = 0        override fun onCreate(context: Context) {        //todo    }}复制代码

然后在 MainApplication 中实现生命周期分发

class MainApplication : Application() {    private val callbacks = mutableListOf()        override fun onCreate() {        super.onCreate()        callbacks.add(HomeAppLifecycle())        callbacks.add(UserAppLifecycle())        // add whatever you want        // 排序实现模块顺序分发        callbacks.sortBy { it.getPriority() }        callbacks.forEach { it.onCreate(this) }    }    }复制代码

这样能实现需求,但每增加一个模块,就得回到MainApplication中添加一个,不够优雅不够装,这时候就可以用上 APT + Transform

实现原理

  1. 各模块创建AppLifecycleCallback实现类,添加注解,apt为其生成代理类,以"_Proxy"结尾
  2. 构造 AppLifecycleManager 对外API,统一管理Proxyc allbacks
  3. gradle transform遍历.class文件,找到"_Proxy"的所有类,保存类路径,使用 asm 将代理类“加入”到AppLifecycleManager中

此方案主要是为了解学习apt,gradle transform,实际上有更好的实现方案。各位大佬畅所欲言,提出此方案的短板

实现过程

一、 AppLifecycle API

创建android-library,定义 AppLifecycleCallbackAppLifecycleManager

interface AppLifecycleCallback {    fun getPriority(): Int = 0    fun onCreate(context: Context)}复制代码

AppLifecycleManager中 onCreate 是对外的API,在MainAppkication中调用; registerAppLifecycleCallback 则是在transform阶段使用asm在AppLifecycleManager的构造方法中调用,将代理类路径传入,最终通过反射存储在callbacks中

object AppLifecycleManager {    private var callbacks: MutableList? = null    fun onCreate(context: Context) {        callbacks?.run {            sortBy { it.getPriority() }            forEach { it.onCreate(context) }        }    }    private fun registerAppLifecycleCallback(name: String) {        try {            if (callbacks == null) {                callbacks = mutableListOf()            }            val instance = Class.forName(name).getConstructor().newInstance()            if (instance is AppLifecycleCallback && !callbacks!!.contains(instance)) {                callbacks!!.add(instance)            }        } catch (e: Exception) {            e.printStackTrace()        }    }}复制代码

二、 APT

  1. 创建kotlin-library,定义annotation

@Target(AnnotationTarget.CLASS)@Retention(AnnotationRetention.SOURCE)annotation class AppLifecycle()复制代码
  1. 创建kotlin-library,定义Processor,获取所有 @AppLifecycle注解类,使用 KotlinPoet 生成Proxy类

@AutoService(Processor::class)class AppLifecycleProcessor : AbstractProcessor() {    // 省略部分代码    override fun process(p0: MutableSet?, environment: RoundEnvironment?): Boolean {        environment?.run {            getElementsAnnotatedWith(AppLifecycle::class.java)                .filter { it.kind == ElementKind.CLASS }                .filter {                    (it as TypeElement).interfaces.contains(                        elements.getTypeElement(callbackName).asType()                    )                }                .forEach {                    AppLifecycleProxyBuilder(it as TypeElement, elements).build().writeTo(filer)                }        }        return true    }}复制代码

KotlinPoet生成Proxy类

class AppLifecycleProxyBuilder(private val typeElement: TypeElement, elements: Elements) {    // 省略部分代码    fun build(): FileSpec {        return FileSpec.builder(packageName, fileName)            .addType(getTypeSpec())            .build()    }    private fun getTypeSpec(): TypeSpec {        return TypeSpec.classBuilder(fileName)            .addProperty(getProperty())            .addSuperinterface(superInterface)            .addFunction(getOnCreate())            .addFunction(getPriority())            .build()    }    private fun getProperty(): PropertySpec {        // 对应注解类实例        return PropertySpec.builder("callback", typeElement.asClassName(), KModifier.PRIVATE)            .initializer("%T()", typeElement.asType())            .build()    }    private fun getOnCreate(): FunSpec {        // onCreate(context: Context)        return FunSpec.builder("onCreate")            .addModifiers(KModifier.OVERRIDE)            .addParameter("context", contextType)            .addStatement("callback.onCreate(context)")            .build()    }    private fun getPriority(): FunSpec {        // getPriority(): Int        return FunSpec.builder("getPriority")            .addModifiers(KModifier.OVERRIDE)            .returns(Int::class)            .addStatement("return callback.getPriority()")            .build()    }}复制代码

注:kotlin使用apt,要在build.gradle要有以下两个声明

plugins {    id 'kotlin-kapt'}dependencies {    kapt (project(":processor"))}复制代码

三、 Gradle Transform

创建kotlin-library

定义AppLifecyclePlugin,继承Transform,实现Project接口

class AppLifecyclePlugin : Transform(), Plugin {    private val appLifecycleClassNames = mutableListOf()    private var appLifecyclesJar:File? = null        override fun transform(transformInvocation: TransformInvocation?) {        // transform中遍历.class文件,类名以“_Proxy”结尾并且实现AppLifecycleCallback        ...        if (name.endsWith(proxySuffix) && classReader.interfaces.contains(callbackInfo)) {            appLifecycleClassNames.add(name)        }        ...        // 定位到包含 ApplifecycleManager的JarEntry        if(jarEntity.name == managerClassFile){            appLifecyclesJar = outputJar        }        // 最终使用ClassVistor将所有Proxy类加入到Manager中的callbacks里        val cv: ClassVisitor = AppLifecycleVisitor(classWriter,appLifecycleClassNames)        classReader.accept(cv, ClassReader.EXPAND_FRAMES)        classWriter.toByteArray()    }}复制代码

AppLifecycleVisitor

class AppLifecycleVisitor(classVisitor: ClassVisitor,private val callbacks: List) : ClassVisitor(Opcodes.ASM9,classVisitor) {    override fun visitMethod(        access: Int,        name: String?,        descriptor: String?,        signature: String?,        exceptions: Array?    ): MethodVisitor {        var visitor = super.visitMethod(access, name, descriptor, signature, exceptions)        if ("" == name && "()V" == descriptor && access and Opcodes.ACC_PRIVATE != 0) {            visitor = object : AdviceAdapter(ASM9, visitor, access, name, descriptor) {                override fun onMethodExit(opcode: Int) {                    for (item in callbacks) {                        mv.visitVarInsn(ALOAD, 0)                        mv.visitLdcInsn(item.replace("/", "."))                        mv.visitMethodInsn(                            INVOKESPECIAL,                            "com/lauter/applifecycle/AppLifecycleManager",                            "registerAppLifecycleCallback",                            "(Ljava/lang/String;)V",                            false                        )                    }                }            }        }        return visitor    }}复制代码

创建完AppLifecyclePlugin,创建文件src/main/resources/META-INF/gradle-plugins/lauter.applifecycle.properties

implementation-class=com.lauter.applifecycle.AppLifecyclePlugin复制代码

到此,工作基本完成。只需要将plugin发布到本地,就可以测试功能了

四、 发布测试

在上文创建的plugin项目下build.gradle中添加:

plugins {    ...    // 添加publish    id 'maven-publish'}...// 发布到本地publishing {    publications {        mavenJava(MavenPublication) {            from components.java            groupId = "io.github.chenlauter"            artifactId = "applifecycle"            version = "1.0"        }    }    repositories {        mavenLocal()        maven {            url = '../local-plugin-repository'        }    }}复制代码

在gradle中执行publish,发布完成后项目中会新增 local-plugin-repository 文件夹

在project的build.gradle中添加依赖

buildscript {    repositories {        ...        // 添加依赖,gradle7.1之后是到setting.gralde-pluginManagement中添加        maven { url './local-plugin-repository' }    }    dependencies {        ...        classpath('io.github.chenlauter:applifecycle:1.0')    }}复制代码

最后在app的build.gradle中添加

plugins {    ...    // 这里跟第三步创建的lauter.applifecycle.properties文件名对应    id 'lauter.applifecycle'}复制代码

至此,所以依赖都配置完毕,运行工程,在app-build下,用Android Studio直接打开apk查看 AppLifecycleManager的字节码,可以看到,在构造方法中已加入两个Proxy类路径调用:

通过查看日志打印,也能看到功能正常

D/AppLifecycle: HomeAppLifecycle onCreate D/AppLifecycle: MainAppLifecycle onCreate复制代码

项目地址

github.com/ChenLauter/…

启发参考

juejin.cn/post/702921…


原文链接:https://juejin.cn/post/7126419784471674887

展开阅读全文

页面更新:2024-04-02

标签:遍历   注解   初始化   生命周期   路径   模块   定义   代码   文件   方案   项目

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号

Top