美文网首页
Android如何设计一个全局可调用的ViewModel对象?

Android如何设计一个全局可调用的ViewModel对象?

作者: 小城哇哇 | 来源:发表于2023-05-15 19:47 被阅读0次

    很多时候我们需要维护一个全局可用的ViewModel,因为这样可以维护全局同一份数据源,且方便使用协程绑定App的生命周期。那如何设计全局可用的ViewModel对象?

    一、思路

    viewModel对象是存储在ViewModelStore中的,那么如果我们创建一个全局使用的ViewModelStore并且在获取viewModel对象的时候从它里面获取就可以了。

    viewModel是通过ViewModelProviderget方法获取的,一般是ViewModelProvider(owner: ViewModelStoreOwner, factory: Factory).get(ViewModel::class.java)

    如何将ViewModelProviderViewModelStore关联起来? 纽带就是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)
            }
        }
    }
    
    

    BaseActivityBaseFragment中调用上述扩展方法

    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
    
    

    相关文章

      网友评论

          本文标题:Android如何设计一个全局可调用的ViewModel对象?

          本文链接:https://www.haomeiwen.com/subject/kscvsdtx.html