# MVVM **Repository Path**: shawn_songxue/MVVM_Practice ## Basic Information - **Project Name**: MVVM - **Description**: 实现 MVVM 项目的代码,践行 Android 的 MAD 开发技术,如 hilt,kotlin,Compose,协程 Coroutine,Room 等等 - **Primary Language**: Android - **License**: Apache-2.0 - **Default Branch**: dev - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2022-03-18 - **Last Updated**: 2025-04-23 ## Categories & Tags **Categories**: Uncategorized **Tags**: Kotlin, Android, MVVM, Coroutine ## README # Android MAD 开发 Demo #### 介绍 本项目基于 MVVM 架构设计,以 WanAndroid 为数据供应平台,践行 Android 的 MAD 开发技术,如 Hilt,Kotlin,Compose,协程(Coroutines),Room,Retrofit+Okhttp,Gradle kts 等等。需要特别指出的是,项目的中的图片等UI样式资源均来自 https://github.com/bytebitx/WanAndroid。 #### 软件架构 项目结构: ![](image/image_msj28m0qc1.png) 技术点: 1. 基于APT 实现多业务模块在 APP 启动时自动初始化。 背景:在多模块开发中,模块中的代码往往依赖到 Application Context 来获取对应的资源。例如在网络模块中,需要 Context 来判断网络的连接状态。在以往的开发中,需要在自定义的 Application 中通过对向所有的子模块注入 application context 来初始化。 ```kotlin class DemoApp : Application() { override fun onCreate() { ... ... initModules() ... ... } fun initModules() { ... ... // 初如化 XX 模块 XXModule.init(this) ... ... } } ``` 可以想象,当项目每新增一个子模块时,就需要在initModules 发法中也新增代码(同理,删除也要同步删掉),这样对开发和调试很不友好。因此,需要一种自动识别子模块和自动执行初始化操作的方法,来实现自动插拔。 方案:参考 ARouter 的实现,使用 APT + KotlinPoet来实现。 ![](image/image_Ndym8MalWp.png) ```kotlin //定义子模块注解 @Retention(AnnotationRetention.BINARY) @Target(AnnotationTarget.CLASS) annotation class AppModel //定义子模块在编译期间动态生成类的超类 interface IModuleInit { fun cacheModuleClassPath() : List } ``` 所有的子模块初始化类中,需要用 @AppModel 来注解,并在自定义的 Processor 中解析和使用 Kotlinpoet 生成动态代码: ![](image/image_NNpWS4P1rx.png) 其过程如下图,代码中的cacheModuleClassPath 方法提供需要初始化的 Class 的 Path。 ![](image/image_CGim2A0SoD.png) 既然动态代码已经生成,接下来需要在 APP 启动过程中识别除他们,获取到 XXModule 的路径,再通过反射来调用XXModule 的初始化发法。以下代码抄自 ARouter,可看他们的源码。 ```kotlin /** * 利用 ARouter 的工具类 ClassUtils,扫描得到以 $packageName 包下的所有 class * @param packageName: "com.jetpack.mvvm.init" */ private fun getFileNameByPackageName(context: Application, packageName: String): Set = runBlocking { val classNames: MutableSet = mutableSetOf() // 用到 ARouter 的工具类 ClassUtils val paths = ClassUtils.getSourcePaths(context) // 扫描得到以 $packageName 包下的所有 class,添加到 classNames 列表。 classNames } ``` 遍历扫描得到的 class list,如 Init\_module\_NetModule。反射得到实例,调用 cacheModuleClassPath 得到真正的 NetModule 的路径,实例化以执行最终的初始化。 ```kotlin fun initModules(context: Application) { val pathList = getFileNameByPackageName(context, GENERATE_PACKAGE) pathList.filter { it.contains("Init_Module_") } .forEach { className -> try { val instance = Class.forName(className).getConstructor().newInstance() if (instance is IModuleInit) { instance.cacheModuleClassPath().forEach { moduleClassName -> val module = Class.forName(moduleClassName) // ... ... 实例化并调用 initModule 方法 //if (obj is InterAppModel) { // obj.initModule(context) // } } } } catch (e: Exception) { Log.e(TAG, "parse $className error", e) } } } ``` 2\. 数据共享,待续...... 3\. Gradle kts (APG 7.0),待续......