使用 动态代理在 Compose 预览中模拟 ViewModel 依赖注入

380 阅读3分钟

(作者很懒,只提供了思路和代码示例,由ChatGPT完成撰写)

在 Jetpack Compose 开发中,Preview 是一个重要的工具,它允许我们在 IDE 中快速预览 UI,而无需运行整个应用程序。然而,预览环境并不能直接注入依赖(例如 Dagger-Hilt 提供的 ViewModel 或其他依赖项)。为了解决这一问题,可以使用 Java 的动态代理来模拟接口的调用,简化 ViewModel 的依赖管理。

动态代理的基本实现

以下代码展示了一个通用的 fake 函数,它使用 Java 动态代理机制为任意接口创建一个模拟对象。在 Preview 中,我们可以通过这个代理对象来快速构造需要依赖的 ViewModel

inline fun <reified T : Any> fake(crossinline handler: (Any?) -> Any? = { null }): T {
    return Proxy.newProxyInstance(
        T::class.java.classLoader,
        arrayOf(T::class.java),
        object : InvocationHandler {
            override fun invoke(proxy: Any?, method: Method?, args: Array<out Any?>?): Any? {
                return handler.invoke(method)
            }
        }
    ) as T
}

这个函数接受一个泛型类型 T,它会为给定的接口生成一个动态代理。通过 handler 参数,你可以自定义方法调用时的行为。例如,记录日志或返回特定值。


示例:在 Preview 中使用动态代理模拟 ViewModel 的依赖

以下是一个完整的示例,通过动态代理为 HomeViewModel 提供虚拟的依赖,从而实现 Compose UI 的预览。

ViewModel 示例

@HiltViewModel
class HomeViewModel @Inject constructor(
    private val accountRepo: AccountRepo,
    private val settingRepo: SettingRepository,
    private val vehicleRepo: VehicleRepo,
) : ViewModel()

Compose 预览函数

@Preview(
    widthDp = SCREEN_WIDTH,
    heightDp = SCREEN_HEIGHT,
    uiMode = UI_MODE_NIGHT_YES
)
@Composable
fun PreviewDarkHome() {
    PreviewContainer {
        HomeScreen(
            HomeViewModel(
                fakeAccountRepo = fake(), // 使用动态代理模拟 AccountRepo
                settingRepo = fake(),    // 使用动态代理模拟 SettingRepository
                vehicleRepo = fake()     // 使用动态代理模拟 VehicleRepo
            )
        )
    }
}

在预览中,我们不需要真实的依赖实现,只需要满足 ViewModel 的构造函数需求即可。通过 fake() 创建的动态代理对象,可以替代实际的依赖。


动态代理的优势

  1. 简化依赖管理:在预览环境中,我们无需配置复杂的依赖注入框架,只需要动态代理即可快速模拟依赖。
  2. 提高开发效率:通过动态代理,开发者可以专注于 UI 开发,而不必等待依赖模块的实现。
  3. 灵活性:可以通过 handler 自定义每个方法的返回值,从而满足特定场景下的测试需求。

注意事项

  • 仅限预览使用:动态代理生成的对象是空实现,不适合作为生产环境中的实际依赖。
  • 不可上线:动态代理生成的对象无法处理复杂的逻辑,且不具备任何真实功能,仅用于满足构造函数和预览的需求。
  • 性能开销:动态代理有一定的性能开销,但在预览环境中影响可以忽略不计。

总结

通过 Java 动态代理和 fake 函数,我们可以在 Jetpack Compose 的预览环境中轻松实现 ViewModel 的依赖注入。这种方法避免了编写繁琐的测试依赖,同时提高了 UI 开发的效率。在实际项目中,只需将真实的依赖替换为动态代理即可完成高效的预览工作。

这种优雅的解决方案不仅适合开发初期的快速迭代,还能为大型项目中的模块化开发提供便捷的支持。