Android架构组件学习:Kotlin实现ViewModel单例getInstance()方法
如何用Kotlin优雅实现ViewModel内的单例
getInstance()风格逻辑 Hey there! 我来帮你把这段代码改成更符合Kotlin风格的写法,同时也贴合Android Architecture Components的最佳实践~
首先先聊聊你现有代码的几个小问题:
- 每次调用
getLocation()都会创建一个新的MyLocationListener实例,这并不是单例逻辑 lateinit var存在潜在的未初始化风险,万一提前访问会抛出异常- 直接传入
Context如果是Activity/Fragment的上下文,很容易引发内存泄漏(ViewModel生命周期比页面长)
下面是几种Kotlin化的解决方案:
方案1:用Kotlin lazy委托实现懒加载单例
这是Kotlin里实现懒加载单例最常用的方式,默认是线程安全的,完美适配ViewModel的场景:
class LocationViewModel(private val application: Application) : ViewModel() { // 第一次调用时才初始化,之后永远返回同一个实例 private val locationData: MyLocationListener by lazy { MyLocationListener(application) // 用Application上下文避免内存泄漏 } fun getLocation(): MyLocationListener { return locationData } }
说明:
- 通过ViewModel构造函数传入
Application,需要配合自定义ViewModelProvider.Factory或者依赖注入框架(比如Hilt)来实现 lazy委托会自动处理初始化逻辑,不用手动判断是否已经创建过实例- 使用
Application上下文是关键,避免ViewModel持有页面上下文导致内存泄漏
方案2:简化写法,直接暴露属性
如果不需要保留getLocation()方法,直接把locationData设为公开属性更简洁:
class LocationViewModel(private val application: Application) : ViewModel() { val locationData: MyLocationListener by lazy { MyLocationListener(application) } }
外部调用时直接用viewModel.locationData即可,完全符合Kotlin的简洁风格。
方案3:配合依赖注入实现全局单例(进阶)
如果你的项目用了Hilt这类依赖注入框架,可以直接把MyLocationListener声明为全局单例,ViewModel里直接注入:
// 先在Hilt模块中定义单例 @Module @InstallIn(SingletonComponent::class) object LocationModule { @Provides fun provideMyLocationListener(application: Application): MyLocationListener { return MyLocationListener(application) } } // ViewModel中直接注入 @HiltViewModel class LocationViewModel @Inject constructor( val locationData: MyLocationListener ) : ViewModel() { // 直接使用locationData即可,无需自己处理初始化 }
这种方式解耦性更强,也更符合现代Android开发的架构理念。
内容的提问来源于stack exchange,提问作者dawzaw
相关产品推荐
相关产品推荐

