经验之谈-ViewModel向Activity/fragment主动获取数据

87 阅读2分钟

再MMVM架构中,activity持有ViewModel引用,ViewModel却不显示持有Activity的引用;这就导致ViewModel只能被动接受来自activity的数据(activity调用viewModel的方法传递数据)。

然而在某些场景下,viewModel需要主动去获取Activity中的数据。例如:

  1. viewModel的某个方法中需要Exoplayer的播放进度值进行逻辑判断。(viewModel不能直接持有Exoplayer/Activity的引用,所以不能主动查询)
  2. 同一Activity中有两个ViewMoel实例,AViewModel的某个方法中需要BViewModel的数据进行逻辑判断。(AViewModel不能直接持有BViewModel的引用,所以不能主动查询)

为了解决该问题,我们可以封装了一个简单的数据提供类(DataProvider)。

使用示例:

//Viewmodel中主动获取Activity数据示例 
class AViewModel : ViewModel(){ 
    val videoProgressDataProvider = DataProvider<Long>() //播放进度dataProvider 
    val vipStateDataProvider = DataProvider<Boolean>() //vip信息dataProvider 
    
    fun adLoadSuccess(){ 
        val videoProgress = videoProgressDataProvider.getData()?:0 //实时获取当前播放进度 
        val isVip = vipStateDataProvider.getData()?:false //获取另一个Viewmodle中vip状态信息 
        if(videoProgress > 5 * 60 * 1000 && !isVip){ 
            //show AD 
        } 
    } 
}
//Activity向aviewModel提供数据 
class DataProviderTestActivity: AppCompatActivity() { 
    private fun initViewModel(){ 
        val aViewModel = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory()).get(AViewModel::class.java) 
        //向aViewModel提供播放进度 
        aViewModel.videoProgressDataProvider.provide(this){ //传入生命周期参数,页面销毁自动清除,防止内存泄漏 
            player.getCurrentPostion() //返回播放器的播放进度;运行在UI线程
        } 
        //向aViewModel提供bViewModel的数据 
        aViewModel.vipStateDataProvider.provide(this){ 
            bViewModel.isVIP() //返回vip状态 
        } 
    } 
}

DataProvider源码:

class DataProvider<T>{
    private var mProvider: Provider<T>? = null
    private val mainHandler = Handler(Looper.getMainLooper())


    fun provide(lifecycleOwner: LifecycleOwner, provider: Provider<T>){
        this.mProvider = provider
        lifecycleOwner.lifecycle.addObserver(object : DefaultLifecycleObserver{
            override fun onDestroy(owner: LifecycleOwner) {
                super.onDestroy(owner)
                lifecycleOwner.lifecycle.removeObserver(this)
                mProvider = null
            }
        })
    }

    fun getData(): T?{
        val isUiThread = Looper.myLooper() == Looper.getMainLooper() //保证mProvider?.onData()在主线程中执行
        if(isUiThread){
            return mProvider?.onData()
        }else{
            val latch = CountDownLatch(1)
            var result: T? = null
            mainHandler.post {
                result = mProvider?.onData()
                latch.countDown()
            }
            latch.await()
            return result
        }
    }
}
注意事项:

1.DataProvider只能有一个最新的数据提供者有效,即provide()方法多次调用的话,最新的一次调用有效。

2.provider.onData()运行在UI线程,便于访问UI控件

完整项目地址:github.com/High-Power-…