如何以及在何处使用 Transformations.switchMap?

Posted

技术标签:

【中文标题】如何以及在何处使用 Transformations.switchMap?【英文标题】:How and where to use Transformations.switchMap? 【发布时间】:2018-05-16 13:54:15 【问题描述】:

在 Google 最近发布的 android 架构组件库中,我们在 Transformations 类中有两个静态函数。虽然map 函数直截了当且易于理解,但我发现很难正确理解switchMap 函数。

switchMap的官方文档可以找到here。

有人可以用一个实际的例子解释一下如何以及在哪里使用 switchMap 函数吗?

【问题讨论】:

另见What is the difference between map() and switchMap() methods?。也许不是 100% 重复,但它有一些非常有用的解释 【参考方案1】:

map() 函数中

LiveData userLiveData = ...;
LiveData userName = Transformations.map(userLiveData, user -> 
     return user.firstName + " " + user.lastName; // Returns String
);

每次userLiveData 的值发生变化,userName 也会随之更新。请注意,我们返回的是 String

switchMap() 函数中:

MutableLiveData userIdLiveData = ...;
LiveData userLiveData = Transformations.switchMap(userIdLiveData, id ->
    repository.getUserById(id)); // Returns LiveData

void setUserId(String userId) 
     this.userIdLiveData.setValue(userId);

每次userIdLiveData 的值改变时,repository.getUserById(id) 都会被调用,就像 map 函数一样。但是repository.getUserById(id) 返回一个LiveData。所以每次repository.getUserById(id)返回的LiveData的值发生变化,userLiveData的值也会发生变化。所以userLiveData的值取决于userIdLiveData的变化和repository.getUserById(id)的值变化。

switchMap() 的实际示例:假设您有一个用户配置文件,其中包含一个关注按钮和一个设置另一个配置文件信息的下一个配置文件按钮。下一个配置文件按钮将使用另一个 ID 调用 setUserId(),因此 userLiveData 将发生变化,UI 也会发生变化。关注按钮将调用 DAO 为该用户再添加一个关注者,因此该用户将拥有 301 个关注者而不是 300 个。userLiveData 将获得来自存储库的此更新,该更新来自 DAO。

【讨论】:

所以基本上,这是一种同时收听数据的几个变化源的方法。如果 id 更改,您的 userLiveData 会更改,如果实际用户的值更改,您的 userLiveData 也会更改。正确的? (您可能可以将一些转换堆叠在一起以将更多 LiveData 连接在一起,尽管您可能也应该使用 MediatorLiveData。) Transformations 的一些架构背景,它们最常用于 ViewModel,因为它允许将来自 DAO 的类型的数据转换为需要在 UI 上显示的类型,所以,想象一下您有一个添加到购物车功能,您将向购物车添加元素,假设这个购物车是一个哈希图,它将每个产品 ID 与列表中的一个项目对应起来,这个哈希图来自 DAO,但不是将此哈希图传递给在 UI 中,我们使用转换将此 hashmap 转换为友好列表以显示在 UI 中,现在 HashMap 将转换为 List 这是一个很好的例子吗?这似乎暗示您在switchMap 回调中进行了数据库调用,而文档声明:“给定函数func 将在主线程上执行。” 如何设置转换查询的默认值? Maarten,我们应该只返回 userLiveData 进行观察,然后对数据库进行实际的异步调用,然后在这个 livedata 上调用 postValue。【参考方案2】:

将我的 2 美分加到 @DamiaFuentes 答案中。

MutableLiveData userIdLiveData = ...;
LiveData userLiveData = Transformations.switchMap(userIdLiveData, id ->
repository.getUserById(id)); // Returns LiveData

void setUserId(String userId) 
     this.userIdLiveData.setValue(userId);

Transformations.switchMap 方法只有在您至少有一个 userLiveData 观察者时才会被调用

【讨论】:

非常感谢@Prakash 我一直在努力找出为什么我的 switchMap 没有监听到触发器的变化。 我曾经是一个简单的人。当我无法理解这个概念时,我就去做了自己的东西。在这种情况下,我创建了一个包含自定义数据源、构建器等的整个类,直到我遇到了心理障碍。你的回答让我再次成为一个简单的人。我删除了那个类。 repository.getUserById(id);在getUserById()上调用switchmap(),mutabledata != null 条件的情况如何处理【参考方案3】:

对于那些想要更多解释下面给出的@DamiaFuentes switchmap() 函数示例的人:

 MutableLiveData userIdLiveData = ...;
 LiveData userLiveData = Transformations.switchMap(userIdLiveData, id ->
     repository.getUserById(id));

 void setUserId(String userId) 
      this.userIdLiveData.setValue(userId);
 

在存储库包含 User(1, "Jane") 和 User(2, "John") 的场景中,当 userIdLiveData 值设置为“1”时,switchMap 将调用 getUser(1),这将返回包含值 User(1, "Jane") 的 LiveData。所以现在,userLiveData 将发出 User(1, "Jane")。当存储库中的用户更新为 User(1, "Sarah") 时,userLiveData 会自动收到通知并发出 User(1, "Sarah")。

当使用 userId = "2" 调用 setUserId 方法时,userIdLiveData 的值会发生变化并自动触发从存储库中获取 id 为 "2" 的用户的请求。因此,userLiveData 发出 User(2, "John")。 repository.getUserById(1) 返回的 LiveData 作为源被移除。

从这个例子中,我们可以理解 userIdLiveData 是触发器,repository.getUserById 返回的 LiveData 是“后备”LiveData。

如需更多参考,请查看:https://developer.android.com/reference/android/arch/lifecycle/Transformations

【讨论】:

Arch 已弃用:使用此developer.android.com/reference/androidx/lifecycle/… 如何为switchmap中的搜索字符串设置默认查询?【参考方案4】:

传递给 switchMap 的函数返回 LiveData。当您的存储库本身返回 LiveData 时使用它。

【讨论】:

【参考方案5】:

另外一点要考虑是否在switchMapmap 之间进行选择,您必须记住map 总是将返回值包装在LiveData 周围,例如

fun getUser(id: Int): User 
...
val userId = MutableLiveData(1)
val user = userId.map  // LiveData<User>
   repository.getUser(it)

如果repository.getUser(it) 返回一个简单的User 对象而不是LiveData,则您可以考虑使用map,因此用户类型变为LiveData&lt;User&gt;

如果repository.getUser(it) 返回LiveData&lt;User&gt;,那么最好使用switchMap

fun getUser(id: Int): LiveData<User>
...
val userId = MutableLiveData(1)
val user = userId.switchMap  // LiveData<User>
   repository.getUser(it)

user 类型将是 LiveData&lt;User&gt;

【讨论】:

【参考方案6】:

还有一点需要理解。 有人可能会认为,由于我们总是在 switchMap() 中返回 LiveData 的新值(新引用),那么我们如何在观察者只设置一次的情况下观察实际值呢? 重点是 Transformations 的返回值。switchMap 是 MediatorLiveData,它将新的 LiveData 引用添加为新源(并停用其他源)。

【讨论】:

以上是关于如何以及在何处使用 Transformations.switchMap?的主要内容,如果未能解决你的问题,请参考以下文章

一篇掌握LiveData transformations

htmlspecialchars() 或 如何以及在何处使用?

Java 中如何以及在何处使用注解?

使用 AsyncStorage 如何以及在何处保存整个 redux 存储

在反应中如何以及在何处获取数据[重复]

多线程环境中的扩展 OVERLAPPED 对象池:在何处以及如何有效地使用锁定