重学安卓:学习 View 事件分发,就像外地人上了黑车!

2019-07-22 13:22:04 +08:00
 KunMinX

很高兴见到你!

今天我要和大家分享的是 View 事件分发。

原本这是 《重学安卓》专栏原定的第 8 篇(鉴于读者的强烈要求,暂时插播了 Jetpack 系列),可没想到的是,我无法忘却 3 年前备受折磨的那个夜晚 —— 在我第一次学习 View 事件分发,却被网文折磨的那个夜晚。

是网上介绍 View 事件分发的文章不够多吗?

不是的,恰恰相反,网上的爆款文章不计其数,待你仔细阅读,却 颇有一种 “外地人上了黑车” 的感觉 —— 一言不合先上 30 张图表,带你在城市外围饶个上百圈,就是不直奔主题 解释一个现象为什么会存在、造成它存在的缘由为何、它如此设计是为了解决什么问题 ……

比起 拨开迷雾、明确状况、建立感性认识,他们更热衷于自我包装。

—— 有没有帮助我不管,先唬住人再说

为了唬人,就算给他人徒添困扰、白费大量时间,也在所不惜!

正是对那次痛苦经历的念念不忘,于是我 破例 将这篇文章分享给大家。

在此,我向 3 年前的那个自己发誓,我必在 结尾 200 字 就讲明白,别人非要绕个 3000、5000 字都讲不明白的事件分发。

不仅如此,我还要额外地帮助大家理解,事件分发流程中的 3 个小细节:之所以如此设计,是出于什么考虑。通过“知其所以然”,来方便大家更好地加深印象。 😉

作者:KunMinX

链接: https://juejin.im/post/5d3140c951882565dd5a66ef

来源:掘金

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

10044 次点击
所在节点    Android
10 条回复
logobaby
2019-07-24 10:21:54 +08:00
事件分发 Handler 原理 都是文章多如繁星 写得好的不多
ckrv2
2019-07-24 10:57:05 +08:00
赞一个
ukyoo
2019-07-30 18:00:06 +08:00
Hi, 有个 JetPack 的问题请教一下, Repository 应当返回 LiveData<T>吗, 假设我使用 RxJava 的 Flowable 作为数据源, 那我应该把 Flowable 转换成 LiveData 的操作 放在 Repository 里还是 ViewModel 里呢.
KunMinX
2019-07-30 21:28:59 +08:00
@ukyoo

我不是这么用的。

LiveData 类似于 Eventbus,它的职责是在 单例的帮助下,从唯一可信源取材完成状态的正确分发。

这里我们将 RxJava 所在的数据源当作唯一可信源,将多个订阅者页面当作分发的目标页面,将 ViewModelProviders 管理的 ViewModel 作为单例。

那么他们的关系是,页面单向依赖于 ViewModel,ViewModel 单向依赖于 数据源。

ViewModel 持有 liveData。ViewModel 通过 数据源的方法来注入 LiveData,在数据源方法处理好数据后,通过 liveData.setValue(数据)来将数据植入到 LiveData,此时 liveData 会在页面的 observe 回调中通知页面做出 UI 响应。

伪代码如下:

class XXXFragment : Fragment{

XXXViewModel = ViewModelProviders.of(this).get(XXXViewModel.class)

XXXViewModel.getXXXLiveData().observe(this,()->{
//4.订阅者页面拿到响应的结果,处理 UI 逻辑
})

//1.通过 ViewModel 去向数据源请求数据
XXXViewModel.requestXXX("id")
}

class XXXViewModel : ViewModel{
MultableLiveData xxxliveData = new MultableLiveData()

public MultableLiveData getXXXLiveData(){
return xxxliveData;
}

public void requestXXX(String id){
//2.ViewModel 中转,去向数据源请求数据
DataRepo.getInstance.requestXXX(getXXXLiveData(),id);
}
}

class DataRepo {

private static DataRepo sInstance;

public void requestXXX(MultableLiveData xxxliveData,String id){
Observable.create(emitter -> {
calculator()...
emitter.onNext(result); }).subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(o -> {

//3.数据源处理好数据,将纯粹的数据注入到 LiveData
xxxliveData.setValue(result);

});
}
}

如果这样说还是不太清晰的话,请等一等,过段时间我贴个 github 项目,专门介绍 Jetpack 的最佳实践。
KunMinX
2019-07-30 21:31:00 +08:00
ukyoo
2019-07-31 10:08:42 +08:00
@KunMinX 多谢回复. 看官方的 browse_sample 里全都是通过 Transformations.switchMap(sourceLiveData)来观察的
```
val results: LiveData<Resource<List<Repo>>> = Transformations
.switchMap(_query) { search ->
if (search.isNullOrBlank()) {
AbsentLiveData.create()
} else {
repoRepository.search(search)
}
}
```
可是实际开发中不一定有输入参数 sourceLiveData,于是我用了非常 ugly 的方法...用一个 LiveData<Unit>来伪装数据源...
```
//点击刷新验证码
private val _refreshVerifyEvent = MutableLiveData<Unit>()

fun clickRefreshVerifyImg() {
_refreshVerifyEvent.value = Unit
}

val verifyImgUrlLiveData: LiveData<Resource<String>> = Transformations.switchMap(_refreshVerifyEvent) {
repository.getVerifyUrl()
}


```
ukyoo
2019-07-31 10:24:52 +08:00
ukyoo
2019-07-31 10:31:55 +08:00
KunMinX
2019-07-31 12:45:55 +08:00
@ukyoo
图片推荐上传 sm.ms ,github 外链在站外除了本人,无法查看。

Transformations 这个相当于是单例,帮助 LiveData 纵横四海,不过有个缺点就是,它无法限制 LiveData 的作用域。当有多个页面需要共享某个 LiveData,或者当某个页面退出时要求清理 LiveData,Transformations 就不太方便。

ViewModel 对作用域有着良好的封装,所以基本上我是用 ViewModel 去管理 LiveData。
ukyoo
2019-07-31 17:37:29 +08:00
@KunMinX 多谢

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/585068

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX