美文网首页Android开发Android开发Android技术知识
Android架构:ViewModel与View之间的通信

Android架构:ViewModel与View之间的通信

作者: Jomeslu | 来源:发表于2018-08-29 10:54 被阅读44次

    背景

    自从Google在I/O上宣布架构组件以来,网上已经有很多关于MVVM架构的讨论,所以许多喜欢Presenters的开发人员已经开始接受ViewModel世界,使用ViewModels可以减少样板代码,在配置更改期间管理数据,使得在多个片段之间共享数据变得容易。然而,它跟View之间的通信就变的更加难了

    问题

    我们举一个编辑配置文件屏幕的例子。在发送到服务器之前,用户数据必须进行数据验证,这意味着Presenter / ViewModel应该能够向View显示/隐藏提示告知用户。此外,如果在配置更改期间,那么也应该通知View。


    1-Atiy0-oRTPDR4GGwjw5cow.png

    Presenters和ViewModel不应包含对视图的引用。

    对于Presenter类,我们通常定义某种契约接口,其中View实现Contract.view和Presenter实现Contract.presenter。现在,Presenter可以轻松地在View上调用方法,而无需任何activity/fragment 实例。

    interface EditProfileContract {
       interface view {  
           fun setProgress(show: Boolean)
           fun showEmptyFirstNameError()
           fun showEmptyLastNameError()
       }
       interface presenter {
           fun saveProfile(firstName: String, lastName: String, bio: String, email: String, city: City, gender: String)
       }
    }
    

    另一方面,ViewModel与View关系是非常松散的。我们通常使用LiveData或RxJava公开数据。一旦View订阅了ViewModel,它就会开始接收更新。如果是将数据传递给Views时,这方式是非常优秀的。但是当ViewModel需要传达View的状态、进度指示器状态、验证/服务器错误提示等等,会出现问题了。

    解决方法

    LiveData / Observable越少越好。所以我们正在寻找的是一种聚合需要传递给View的信息的方法。在大多数情况下,ViewModel需要传达三件事:
    一:Data →这是需要在View上显示的内容,大多数代码示例都显示了如何将数据从ViewModel传递到View。
    二、Status→这是传给View的错误状态如验证错误、服务器错误等等
    三、State →UI状态,如进度指示器,对话框,每次开始观察ViewModel数据时都应传递给View。我们只需创建一个数据类来保存状态。

    为了简洁起见,Data 我就不讲了大家都知道,我将其遗漏,主要讲讲 status 和state的设计

    对于status的设计
    enum class Status {
       SUCCESS,
       ERROR,
       NO_NETWORK,
       EMPTY_FIRST_NAME,
       EMPTY_LAST_NAME,
       EMPTY_CITY,
       INVALID_URI
    }
    

    LiveData 不提供任何开箱的接口,但是创建一个名为SingleLiveEvent的实例,将LiveData状态公开给View层。可以参考Google的例子SingleLiveEvent

    private val status = SingleLiveEvent<Status>()
    fun getStatus(): LiveData<Status> {
       return status
    }
    fun handleImage(intent: Intent?) {
       intent?.data?.let {
           avatar.value = it.toString()
       } ?: run { status.value = Status.INVALID_URI }
    }
    

    View只需要观察状态LiveData并根据不同的状态/错误执行操作,在这个例子中,我们可以轻松地为每个错误显示不同的toast / snackbar。

    viewModel.getStatus().observe(this, Observer { handleStatus(it) })
    private fun handleStatus(status: Status?) {
       when (status) {
           Status.EMPTY_FIRST_NAME -> Toast.makeText(activity, "Please enter your first name!", Toast.LENGTH_SHORT).show()
           Status.EMPTY_LAST_NAME -> Toast.makeText(activity, "Please enter your last name", Toast.LENGTH_SHORT).show()
           Status.EMPTY_CITY -> Toast.makeText(activity, "Please choose your home city", Toast.LENGTH_SHORT).show()
           Status.INVALID_URI -> Toast.makeText(activity, "Unable to load the photo", Toast.LENGTH_SHORT).show()
           Status.SUCCESS -> {
               startActivity(HomeFragment.newIntent(activity))
               activity.finish()
           }
           else -> Toast.makeText(activity, "Something went wrong, please try again!", Toast.LENGTH_SHORT).show()
       }
    }
    
    对于State的设计

    每次开始观察ViewModel数据时都应传递给View。我们只需创建一个数据类来保存状态。

    data class EditProfileState(
       var isProgressIndicatorShown: Boolean = false,
       var isCityDialogShown: Boolean = false,
       var isGenderDialogShown: Boolean = false)
    

    在ViewModel中将状态创建为MutableLiveData。由于我们只将LiveData暴露给View层,我们还应该为View提供setter来更新它的状态。

    private val state = MutableLiveData<EditProfileState>()
    fun getState(): LiveData<EditProfileState> {
       return state
    }
    fun setProgressIndicator(isProgressIndicatorShown: Boolean) {
       state.value?.isProgressIndicatorShown = isProgressIndicatorShown
    }
    fun setCityDialogState(isCityDialogShown: Boolean) {
       state.value?.isCityDialogShown = isCityDialogShown
    }
    fun setGenderDialogState(isGenderDialogShown: Boolean) {
       state.value?.isGenderDialogShown = isGenderDialogShown
    }
    

    根据状态数据在View图层中显示或隐藏City / Gender对话框。

    总结

    聚合信息(加载status,UIstate,errors)将有助于保持ViewModel的精简和清洁。、

    推荐一下:
    Android博客周刊 :每周分享国内外热门技术博客以及优秀的类库、Google视频、面试经历。
    最新源码汇总:每周分享新的开源代码,有效果图,更直观。
    请关注公众号,有惊喜。

    相关文章

      网友评论

      • 再见信仰:这是翻译的文章吧?为什么不标明翻译&出处?

      本文标题:Android架构:ViewModel与View之间的通信

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