0
点赞
收藏
分享

微信扫一扫

Android组件内核之间组件间通信方案(四)下篇,安卓开发快速上手

王栩的文字 2022-03-20 阅读 114

}

interface presenter {

fun saveProfile(firstName: String, lastName: String, bio: String, email: String, city: City, gender: String)
}
}

但是,在 MVVM 架构中,ViewModel 不再持有 View 的引用,而是通过 [LiveData]( ) 或 [RxJava]( ) 向 View 层暴露数据。一旦 View 订阅了 ViewModel,它就开始接收数据更新。这看似很完美,但当 ViewModel 想要更新 View 状态,比如显示和取消 Loading,将数据校验或服务器结果反馈到 UI 界面上,会变得非常困难。

解决方案

ViewModel 中的 LiveData 或 [Observable]( ) 越少越好。因此我们最好找到一种方法,可以封装需要传递给 View 层的数据和信息。在多数情况下,ViewModel 需要向 View 层暴露以下三种数据:

  • Data
  • Status
  • State
    下面将依次介绍。

Data

Data -- 就是需要在 View 上展示的内容,比如用户信息的 User 实体类,或社交 Feed 流中的列表项。

val user = MutableLiveData<User>()
val feeds = MutableLiveData<List<Feed>>()

Status

Status -- 可以是任何仅需传递一次的信息,如校验错误,网络异常,或者服务器错误。
[Status.Kt]( ):

enum class Status {
SUCCESS,
ERROR,
NO_NETWORK,
EMPTY_FIRST_NAME,
EMPTY_LAST_NAME,
EMPTY_CITY,
INVALID_URI
}

LiveData 没有提供任何开箱即用的方法,但在 Google 的官方示例中,有一个 [SingleLiveEvent]( ) 的实现,可以解决这个问题。

一个生命周期感知的被观察者,仅在订阅后发送新的更新,常用于导航和 Snackbar 消息等事件。
这可以避免一些常见问题:在配置变更(如屏幕旋转)期间,如果观察者处于活动动态,SingleLiveEvent 将会发送更新事件。
它继承于 MutableLiveData,是一个被观察者,即使对外暴露了 SingleLiveEvent#setValue()SingleLiveEvent#call() 方法,
注意:只有一个观察者会受到更新通知。

新建一个 SingleLiveEvent 用来向 View 层暴露 Status 数据。
[EditProfileViewModel.Kt]( ):

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 只关心 Status 数据,并根据不同的状态或错误执行对应的逻辑。如下实例,我们能很方便地根据每个错误显示不同的 Toast 或 Snackbar。
[EditProfileFragment.Kt]( ):

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

State -- 即 UI 状态,比如加载进度条和 Dialog 等,每次开始订阅 ViewModel 的数据时,ViewModel 应该把这些 UI 状态通知给 View 层。一种简单的做法是,我们可以创建一个数据类来保存这些状态。
[EditProfileState.Kt]( ):

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

然后在 ViewModel 中创建一个 MutableLiveData,用来

Android组件内核之间组件间通信方案(四)下篇,安卓开发快速上手

包装这个 EditProfileState。由于 ViewModel 只会暴露 LiveData 给 View 层,因此我们应该提供 setter 方法,便于 View 更新此状态。
[EditProfileViewModel.kt]( ):

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
}

最后,根据上面的 State 状态数据,决定 Dialog 的显示和取消。
[EditProfileFragment.Kt]( ):

viewModel.getState().observe(this, Observer { handleState(it) })

private fun handleState(state: EditProfileState?) {
if (state?.isCityDialogShown == true) {
showCitySelectionDialog()
return
}
if (state?.isGenderDialogShown == true) {
showGenderSelectionDialog()
return
}
}

#二、EventBus源码分析
##2.1EventBus简介

2.1.1EventBus

EventBus 是一个 Android 事件发布/订阅框架,通过解耦发布者和订阅者简化Android 事件传递,这里的事件可以理解为消息,本文中统一称为事件。事件传递既可用于 Android 四大组件间通讯,也可以用户异步线程和主线程间通讯等等。

传统的事件传递方式包括:Intent、Handler、BroadCastReceiver、Interface 回调,相比之下 EventBus 的优点是代码简洁,使用简单,并将事件发布和订阅充分解耦。可简化 Activities, Fragments, Threads, Services 等组件间的消息传递,可替代Intent、Handler、BroadCast、接口等传统方案,更快,代码更小,50K 左右的 jar 包,代码更优雅,彻底解耦。

2.1.2概念

事件(Event):又可称为消息,本文中统一用事件表示。其实就是一个对象,可以是网络请求返回的字符串,也可以是某个开关状态等等。事件类型(EventType)指事件所属的 Class。

事件分为一般事件和 Sticky 事件,相对于一般事件,Sticky 事件不同之处在于,当事件发布后,再有订阅者开始订阅该类型事件,依然能收到该类型事件最近一个Sticky事件。

订阅者(Subscriber):订阅某种事件类型的对象。当有发布者发布这类事件后,EventBus 会执行订阅者的onEvent 函数,这个函数叫事件响应函数。订阅者通过register 接口订阅某个事件类型,unregister 接口退订。订阅者存在优先级,优先级高的订阅者可以取消事件继续向优先级低的订阅者分发,默认所有订阅者优先级都为0。

发布者(Publisher):发布某事件的对象,通过 post 接口发布事件。

2.1.3、订阅者、发布者、EventBus 关系图
EventBus 负责存储订阅者、事件相关信息,订阅者和发布者都只和 EventBus 关联。

写在最后

很多人在刚接触这个行业的时候或者是在遇到瓶颈期的时候,总会遇到一些问题,比如学了一段时间感觉没有方向感,不知道该从哪里入手去学习,对此我整理了一些资料

如果你熟练掌握以下列出的知识点,相信将会大大增加你通过前两轮技术面试的几率!这些内容都供大家参考,互相学习。

①「Android面试真题解析大全」PDF完整高清版+②「Android面试知识体系」学习思维导图压缩包,最后觉得有帮助、有需要的朋友可以点个赞

Android组件内核之间组件间通信方案(四)下篇,安卓开发快速上手

Android组件内核之间组件间通信方案(四)下篇,安卓开发快速上手

Android组件内核之间组件间通信方案(四)下篇,安卓开发快速上手

举报

相关推荐

0 条评论