在Android多模块项目间获取接口实例的方法

1,484 阅读2分钟

Discovery

通过KSPAGP实现的在Android工程多模块之间获取接口的实例对象的辅助工具。

通过在接口上添加@Discoverable注解后,在工程中的任意模块中通过Discoveries类获取该接口的实例,辅助开发者在模块之间访问数据。

Github: github.com/xiazunyang/…

原理

Discovery由3个模块构成,分别是基于Kotlin Symbol Processor的注解处理器模块、基于Android Gradle Plugin的插件模块以及一个kotlin库模块。

  • kotlin模块

    • 包含一个Discovrable注解,及一个Discovries类。
  • KSP模块

    • 在编译前,获取所有被Discoverable注解标记的接口的信息,生成一个列表并记录下来。
  • AGP模块

    • 在编译时,扫描每个模块中的类文件,并将上述列表中接口的实现类通过ASM注册到kotlin模块的Discoveries类中。

安装

  1. 在根模块的build.gradle的适当位置添加以下代码:

    buildscript {
       repositories {
           google()
           mavenCentral()
           gradlePluginPortal()
       }
       dependencies {
           //添加Discovery插件
           classpath("cn.numeron:discovery.plugin:1.0.0")
           //添加KSP插件
           classpath("com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin:1.5.21-1.0.0-beta06")
       }
    }
    
  2. 在需要使用@Discoverable注解的模块中的build.gradle文件中添加以下代码:

    plugins {
        id("com.android.library")
        ...
        //应用KSP插件
        id("com.google.devtools.ksp")
    }
    
    ...
    
    dependencies {
        ...
        //应用Discovery的KSP插件
        ksp("cn.numeron:discovery.ksp:1.0.0")
        //添加Discovery library库
        implementation("cn.numeron:discovery.library:1.0.0")
    }
    
    ...
    
    ksp {
        //设置此模块的唯一标识和根项目的编译目录
        arg("projectName", "module-name")
        arg("rootProjectBuildDir", rootProject.buildDir.absolutePath)
    }
    
  3. 在主模块的build.gradle文件中添加以下代码:

    plugins {
        id("com.android.application")
        ...
        //应用Discovery插件
        id("discovery")
    }
    

使用

  • 获取其它模块的业务服务

    1. 在接口上使用@Discovrable注解
    @Discoverable
    interface ISignInService {
    
        /** 判断当前是否已登录 */
        suspend fun isSignIn(context: Context): Boolean
    
        /** 通过用户名和密码进行登录 */
        suspend fun signInByPassword(username: String, password: String)
    
    }
    
    1. 在任意模块中实现ISignInService接口
    class SignInServiceImpl: ISignInService {
    
        override suspend fun isSignIn(context: Context): Boolean {
            TODO("判断是否已经登录")
        }
    
        override suspend fun signInByPassword(username: String, password: String) {
            TODO("根据提供的账号密码进行登录")
        }
    
    }
    
    1. 在任意模块的代码中通过Discoveries获取接口实例
    lifecycleScope.launch {
        val signInService = Discoveries.getInstance<ISignInService>()
        if (!signInService.isSignIn(requireContext())) {
            //未登录, do something...
        }
    }
    
  • 获取所有模块中的所有实例

    1. 在基础模块中声明初始化接口
    @Discoverable
    interface IInitiator {
    
        fun init(application: Application)
    
    }
    
    1. 在其它模块中实现该接口
    //需要初始化的A模块
    class AModuleInitiator: IInitiator {
        override fun init(application: Application) {
            //init a module
        }
    }
    
    //需要初始化的B模块
    class BModuleInitiator: IInitiator {
        override fun init(application: Application) {
            //init b module
        }
    }
    
    1. Application中获取所有实例并初始化
    class MyApplication: Application() {
    
        override fun onCreate() {
            //获取所有IInitiator的实现,并执行init方法
            val initiatorList = Discoveries.getAllInstances<IInitiator>()
            initiatorList.forEach {
                it.init(this)
            }
        }
    
    }