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

 更新时间:2023年05月17日 11:22:28   作者:TimeFine  
很多时候我们需要维护一个全局可用的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对象的详细内容,更多关于Android ViewModel对象的资料请关注脚本之家其它相关文章!

相关文章

  • 详解android使用ItemDecoration 悬浮导航栏效果

    详解android使用ItemDecoration 悬浮导航栏效果

    本篇文章主要介绍了Android 最流行的吸顶效果的实现及代码,非常具有实用价值,需要的朋友可以参考下。
    2017-01-01
  • Android ListView滑动改变标题栏背景渐变效果

    Android ListView滑动改变标题栏背景渐变效果

    这篇文章主要为大家详细介绍了Android ListView滑动改变标题栏背景渐变效果,透明转变成不透明,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-07-07
  • Android电池电量监听的示例代码

    Android电池电量监听的示例代码

    本篇文章主要介绍了Android电池电量监听的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-10-10
  • Android利用滑动菜单框架实现滑动菜单效果

    Android利用滑动菜单框架实现滑动菜单效果

    这篇文章主要介绍了Android实现滑动菜单特效之滑动菜单框架完全解析,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-05-05
  • Android笔记之:App列表之下拉刷新的使用

    Android笔记之:App列表之下拉刷新的使用

    本篇文章介绍了,在Android中App列表之下拉刷新的使用。需要的朋友参考下
    2013-04-04
  • android里面屏蔽home键/禁止Home键或者随你DIY

    android里面屏蔽home键/禁止Home键或者随你DIY

    可以先禁止Home键,再在onKeyDown里处理按键值,点然后在击Home键的时候就把程序关闭,或者随你DIY等等,感觉你可以随心所欲吧,再接再厉,希望本文可以帮助到你
    2013-01-01
  • Android RxJava与Retrofit结合使用详解

    Android RxJava与Retrofit结合使用详解

    RxJava和Retrofit的结合使用估计已经相当普遍了,自己工作中也是一直都在使用。在使用的过程中我们都会对其进行封装使用,GitHub上也有很多封装好的项目可以直接拿来使用,其实对于开源框架的二次封装有时候针对不同的业务逻辑封装的过程中也多多少少有些不同
    2023-03-03
  • android查看网络图片的实现方法

    android查看网络图片的实现方法

    这篇文章主要为大家详细介绍了android查看网络图片的实现方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-04-04
  • Android Keystore签名文件全解析与安全防护

    Android Keystore签名文件全解析与安全防护

    Keystore是存储加密密钥、证书和签名信息的文件(常见格式为.keystore 和.jks),是Android应用签名的核心,用于验证应用来源的可靠性,保障分发过程中的完整性,本文给大家介绍Android Keystore签名文件全解析与安全防护,感兴趣的朋友一起看看吧
    2025-08-08
  • android 软键盘的POPUP布局的问题解决

    android 软键盘的POPUP布局的问题解决

    这篇文章主要介绍了android 软键盘的POPUP布局的问题解决,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-10-10

最新评论