很多时候我们需要维护一个全局可用的ViewModel,因为这样可以维护全局同一份数据源,且方便使用协程绑定App的生命周期。那如何设计全局可用的ViewModel对象?
一、思路
viewModel
对象是存储在ViewModelStore
中的,那么如果我们创建一个全局使用的ViewModelStore
并且在获取viewModel
对象的时候从它里面获取就可以了。
viewModel
是通过ViewModelProvider
的get
方法获取的,一般是ViewModelProvider(owner: ViewModelStoreOwner, factory: Factory).get(ViewModel::class.java)
。
如何将ViewModelProvider
与ViewModelStore
关联起来? 纽带就是ViewModelStoreOwner
, ViewModelStoreOwner
是一个接口,需要实现getViewModelStore()
方法,而该方法返回的就是ViewModelStore
:
public interface ViewModelStoreOwner {
/**
* Returns owned {@link ViewModelStore}
*
* @return a {@code ViewModelStore}
*/
@NonNull
ViewModelStore getViewModelStore(); //返回一个ViewModelStore
}
让某个类实现这个接口,重写方法返回我们定义的ViewModelStore
就可以了。
至于上面ViewModelProvider
构造方法的第二个参数Factory
是什么呢?
源码中提供了二种Factory
,一种是NewInstanceFactory
,一种是AndroidViewModelFactory
,它们的主要区别是:
-
NewInstanceFactory创建ViewModel时,会为每个Activity或Fragment创建一个新的ViewModel实例,这会导致ViewModel无法在应用程序的不同部分共享数据。(ComponentActivity源码getDefaultViewModelProviderFactory方法)
-
AndroidViewModelFactory可以访问应用程序的全局状态,并且ViewModel实例可以在整个应用程序中是共享的。
根据我们的需求,需要用的是AndroidViewModelFactory。
二、具体实现
1、方式一:可以全局添加和获取任意ViewModel
定义Application,Ktx.kt
文件
import android.app.Application
lateinit var appContext: Application
fun setApplicationContext(context: Application) {
appContext = context
}
定义全局可用的ViewModelOwner
实现类
object ApplicationScopeViewModelProvider : ViewModelStoreOwner {
private val eventViewModelStore: ViewModelStore = ViewModelStore()
override fun getViewModelStore(): ViewModelStore {
return eventViewModelStore
}
private val mApplicationProvider: ViewModelProvider by lazy {
ViewModelProvider(
ApplicationScopeViewModelProvider,
ViewModelProvider.AndroidViewModelFactory.getInstance(appContext)
)
}
fun <T : ViewModel> getApplicationScopeViewModel(modelClass: Class<T>): T {
return mApplicationProvider.get(modelClass)
}
}
定义一个ViewModel
通过StateFlow
定义发送和订阅事件的方法
class EventViewModel : ViewModel() {
private val mutableStateFlow = MutableStateFlow(0)
fun postEvent(state: Int) {
mutableStateFlow.value = state
}
fun observeEvent(scope: CoroutineScope? = null, method: (Int) -> Unit = {
_ -> }) {
val eventScope = scope ?: viewModelScope
eventScope.launch {
mutableStateFlow.collect {
method.invoke(it)
}
}
}
}
定义一个调用的类
object FlowEvent {
//发送事件
fun postEvent(state: Int) {
ApplicationScopeViewModelProvider.getApplicationScopeViewModel(EventViewModel::class.java)
.postEvent(state)
}
//订阅事件
fun observeEvent(scope: CoroutineScope? = null, method: (Int) -> Unit = {
_ -> }) {
ApplicationScopeViewModelProvider.getApplicationScopeViewModel(EventViewModel::class.java)
.observeEvent(scope, method)
}
}
测试代码如下:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//打印协程名称
System.setProperty("kotlinx.coroutines.debug", "on")
FlowEvent.observeEvent {
printMsg("MainActivity observeEvent before :$it")
}
//修改值
FlowEvent.postEvent(1)
FlowEvent.observeEvent {
printMsg("MainActivity observeEvent after :$it")
}
}
}
//日志
内容:MainActivity observeEvent before :0 线程:main @coroutine#1
内容:MainActivity observeEvent before :1 线程:main @coroutine#1
内容:MainActivity observeEvent after :1 线程:main @coroutine#2
2、方式二:更方便在Activity和Fragment中调用
定义Application,让BaseApplication
实现ViewModelStoreOwner
//BaseApplication实现ViewModelStoreOwner接口
class BaseApplication : Application(), ViewModelStoreOwner {
private lateinit var mAppViewModelStore: ViewModelStore
private var mFactory: ViewModelProvider.Factory? = null
override fun onCreate() {
super.onCreate()
//设置全局的上下文
setApplicationContext(this)
//创建ViewModelStore
mAppViewModelStore = ViewModelStore()
}
override fun getViewModelStore(): ViewModelStore = mAppViewModelStore
/**
* 获取一个全局的ViewModel
*/
fun getAppViewModelProvider(): ViewModelProvider {
return ViewModelProvider(this, this.getAppFactory())
}
private fun getAppFactory(): ViewModelProvider.Factory {
if (mFactory == null) {
mFactory = ViewModelProvider.AndroidViewModelFactory.getInstance(this)
}
return mFactory as ViewModelProvider.Factory
}
}
Ktx.kt
文件也有变化,如下
lateinit var appContext: Application
fun setApplicationContext(context: Application) {
appContext = context
}
//定义扩展方法
inline fun <reified VM : ViewModel> Fragment.getAppViewModel(): VM {
(this.requireActivity().application as? BaseApplication).let {
if (it == null) {
throw NullPointerException("Application does not inherit from BaseApplication")
} else {
return it.getAppViewModelProvider().get(VM::class.java)
}
}
}
//定义扩展方法
inline fun <reified VM : ViewModel> AppCompatActivity.getAppViewModel(): VM {
(this.application as? BaseApplication).let {
if (it == null) {
throw NullPointerException("Application does not inherit from BaseApplication")
} else {
return it.getAppViewModelProvider().get(VM::class.java)
}
}
}
在BaseActivity
和BaseFragment
中调用上述扩展方法
abstract class BaseActivity: AppCompatActivity() {
//创建ViewModel对象
val eventViewModel: EventViewModel by lazy {
getAppViewModel() }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
}
abstract class BaseFragment: Fragment() {
//创建ViewModel对象
val eventViewModel: EventViewModel by lazy {
getAppViewModel() }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
}
测试代码
class MainActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//打印协程名称
System.setProperty("kotlinx.coroutines.debug", "on")
eventViewModel.observeEvent {
printMsg("MainActivity observeEvent :$it")
}
findViewById<AppCompatButton>(R.id.bt).setOnClickListener {
//点击按钮修改值
eventViewModel.postEvent(1)
//跳转到其他Activity
Intent(this, TwoActivity::class.java).also {
startActivity(it) }
}
}
}
class TwoActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_two)
eventViewModel.observeEvent {
printMsg("TwoActivity observeEvent :$it")
}
}
}
日志
内容:MainActivity observeEvent :0 线程:main @coroutine#1
内容:MainActivity observeEvent :1 线程:main @coroutine#1
内容:TwoActivity observeEvent :1 线程:main @coroutine#2
最后
如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。
如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。
全套视频资料:
一、面试合集
二、源码解析合集
三、开源框架合集
欢迎大家一键三连支持,若需要文中资料,直接扫描文末CSDN官方认证微信卡片免费领取↓↓↓