1

在我的数据库中,我有一个名为的表Account,看起来有点像这样

@Entity(tableName = "accounts", primaryKeys = ["server_id", "account_id"])
data class Account(

    @ColumnInfo(name = "server_id")
    val serverId: Long,

    @ColumnInfo(name = "account_id")
    val accountId: Int,

    @ColumnInfo(name = "first_name", defaultValue = "")
    var firstname: String
)

所以假设我们有以下数据库快照

server_id account_id first_name 1 10 Zak 1 11 Tom 1 12 Bob 1 13 Jim 1 14 Mike

现在我也有以下 POJO,它代表 chatRoom 内的可用视频室

data class RoomInfo(
    @SerializedName("m")
    val participantIntList: List<Int>,
    @SerializedName("o")
    val roomId: String,
    @SerializedName("s")
    val status: Int
)

所以我从我的 Socket 收到一个传入的响应,如下所示

[
   {"m": [10, 11, 12], "o": "room_technical", "s": 1}, 
   {"m": [13, 14], "o": "room_operation", "s": 1}
]

我将它映射到一个列表中,所以我有

val roomInfo: LiveData<List<RoomInfo>> = socketManager.roomInfo

// So the value is basically the json converted to a list of RoomInfos using Gson

为了向用户显示这个可用的房间列表,我需要将 m(即现在房间内的成员)从 accountIds 转换为 account.firstnames。

所以我最终想要的是一个名为 RoomInfoItem 的新对象的列表,它将保存房间列表,其中 accountIds 从数据库的 Account 表中转换为 firstNames。

data class RoomInfoItem(
    val roomInfo: RoomInfo,
    val participantNames: List<String>
)

因此,如果我们进行转换,我们需要得到以下结果

RoomInfo (
      // RoomInfo 
      {"m": [10, 11, 12], "o": "room_technical", "s": 1}, 
      // Participant names
      ["Zak", "Tom", "Bob"]
   )

   RoomInfo (
      // RoomInfo 
       {"m": [13, 14], "o": "room_operation", "s": 1}, 
      // Participant names
      ["Jim", "Mike"]
   )

我的活动需要使用 RoomInfoItems 观察 LiveData,所以我想要的是 LiveData<List> 以将其转换为 LiveData<List>。我怎样才能做到这一点?

4

2 回答 2

0

好吧,最后我找不到解决方案,但我认为我想要实现的目标无法使用 Transformation.switchMap 或 Transformation.map 完成

于 2021-06-15T14:40:35.057 回答
0

据我了解,您想在我的示例中LiveData<List<RoomInfoItem>>类推 。LiveData<List<ResultData>>你有下一个条件:你想观察列表中RoomInfo的每个RoomInfo你想观察participantNames的。(您映射到的每一对RoomInfo和参与者名称 RoomInfoItem)。我认为您可以通过使用MediatorLiveData来实现这种行为。我在下面展示了如何执行此操作的示例:

// For example we have method which returns liveData of List<String> - analogy to your List<RoomInfo>
fun firstLifeData(): LiveData<List<String>> {
    //TODO
}

// and we have method which returns liveData of List<Int> - analogy to your participantNames(List<String>)
fun secondLifeData(param: String): LiveData<List<Int>> {
    //TODO
}

//and analogy of your RoomInfoItem
data class ResultData(
    val param: String,
    val additionalData: List<Int>
)

然后我将展示我实现组合 liveDatas 的想法:

@MainThread
fun <T> combinedLiveData(liveDatas: List<LiveData<T>>): LiveData<List<T>> {
    val mediatorLiveData = MediatorLiveData<List<T>>()
    // cache for values which emit each liveData, where key is an index of liveData  from input [liveDatas] list
    val liveDataIndexToValue: MutableMap<Int, T> = HashMap()
    // when [countOfNotEmittedLifeDatas] is 0 then each liveData from [liveDatas] emited value
    var countOfNotEmittedLifeDatas = liveDatas.size
    liveDatas.forEachIndexed { index, liveData ->
        mediatorLiveData.addSource(liveData) { value ->
            // when liveData emits first value then mack it by decrementing of countOfNotEmittedLifeDatas
            if (!liveDataIndexToValue.containsKey(index)) {
                countOfNotEmittedLifeDatas--
            }
            liveDataIndexToValue[index] = value
            // when countOfNotEmittedLifeDatas is 0 then all liveDatas emits at least one value
            if (countOfNotEmittedLifeDatas == 0) {
                // then we can push list of values next to client-side observer
                mediatorLiveData.value = liveDataIndexToValue.toListWithoutSavingOrder()
            }
        }
    }
    return mediatorLiveData
}

fun <V> Map<Int, V>.toListWithoutSavingOrder(): List<V> = this.values.toList()

/**
 *  Key should be an order
 */
fun <V> Map<Int, V>.toListWithSavingOrder(): List<V> = this.entries.sortedBy { it.key }.map { it.value }
/*
or you can run [for] cycle by liveDataIndexToValue in [combinedLiveData] method or apply [mapIndexed] like:

    liveDatas.mapIndexed{ index, _ ->
        liveDataIndexToValue[index]
    }
to receive ordered list.
 */


以及如何一起使用所有这些:

fun resultSample(): LiveData<List<ResultData>> {
    return firstLifeData().switchMap { listOfParams ->
        val liveDatas = listOfParams.map { param -> secondLifeData(param).map { ResultData(param, it) } }
        combinedLiveData(liveDatas)
    }
}

// u can add extension function like:
fun <T> List<LiveData<T>>.combined(): LiveData<List<T>> = combinedLiveData(this)

// and then use it in this way
fun resultSample_2(): LiveData<List<ResultData>> = firstLifeData().switchMap { listOfParams ->
    listOfParams.map { param -> secondLifeData(param).map { ResultData(param, it) } }.combined()
}

我建议你考虑使用房间的Relations。我认为通过房间的关系你可以得到LiveData<RoomInfoItem>。我无法为您提供有关此方法的更多详细信息,因为目前我不知道有关您的数据方案和域的详细信息。

于 2021-06-17T15:03:34.297 回答