【问题标题】:How to observe Repository LiveData from Fragment via ViewModel如何通过 ViewModel 从 Fragment 观察 Repository LiveData
【发布时间】:2019-10-31 09:12:42
【问题描述】:

我很难弄清楚如何在@GET 请求的情况下连接我的Repository 和ViewModel 的实时数据并在片段中观察它们。

当请求类型为 @POST 时,我没有这个问题,因为我可以在主体上使用 Transformation.switchMap,并且每当主体更改时,存储库的函数就会被调用并向响应实时数据发出值,类似于这样

val matchSetsDetail: LiveData<Resource<MatchDetailBean>> = Transformations.switchMap(matchIdLiveData) { matchId ->
        val body = MatchSetRequest(matchId)
        repository.getMatchSet(body)
    }

但在@GET 请求的情况下,我的视图提供了几个查询参数

我在存储库类中有这个改造 API 调用,代码如下所示

class Repository {
    fun checkInCheckOutUser(apiKey: String, userId: Int, status: String, latitude: Double, longitude: Double, checkedOn: Long): LiveData<Resource<BaseResponse>> = liveData {
            emit(Resource.Loading())
            try {
                val response: Response<BaseResponse> = ApiClient.coachApi.checkInCheckOutUser(apiKey, userId, status, latitude, longitude, checkedOn)
                if (response.isSuccessful && response.body() != null) {
                    if (response.body()!!.isValidKey && response.body()!!.success) {
                        emit(Resource.Success(response.body()!!))
                    } else {
                        emit(Resource.Failure(response.body()!!.message))
                    }
                } else {
                    emit(Resource.Failure())
                }
            } catch (e: Exception) {
                emit(Resource.Failure())
            }
        }
}

和ViewModel

class CheckInMapViewModel : ViewModel() {
    val checkInResponse: LiveData<Resource<BaseResponse>> = MutableLiveData()

        fun checkInCheckOut(apiKey: String, userId: Int, status: String, latitude: Double, longitude: Double, checkedOn: Long): LiveData<Resource<BaseResponse>> {
            return repository.checkInCheckOutUser(apiKey,userId,status,latitude,longitude,checkedOn)
        }
    }

主要问题是我想观察checkInResponse,就像我在@POST请求的情况下观察一样,但不知道如何传递观察存储库LiveData,就像我使用Transformations.switchMap在上面的帖子请求中所做的那样.谁能帮我处理这个案子?

编辑 - 这是我要求的改造服务类

interface CoachApi {
    @POST(Urls.CHECK_IN_CHECK_OUT_URL)
    suspend fun checkInCheckOutUser(
        @Query("apiKey") apiKey: String,
        @Query("userId") userId: Int,
        @Query("status") status: String,
        @Query("latitude") latitude: Double,
        @Query("longitude") longitude: Double,
        @Query("checkedOn") checkedOn: Long
    ): Response<SelfCheckResponse>

    @POST(Urls.SELF_CHECK_STATUS)
    suspend fun getCheckInStatus(
        @Query("apiKey") apiKey: String,
        @Query("userId") userId: Int
    ): Response<SelfCheckStatusResponse>
}

【问题讨论】:

  • 向我们展示改造 API 接口
  • @coroutineDispatcher 添加。有什么帮助吗?

标签: android kotlin android-architecture-components android-livedata kotlin-coroutines


【解决方案1】:

Transformations.switchMap() 只使用MediatorLiveData。由于你的用例有点不同,你可以直接自己实现。

class CheckInMapViewModel : ViewModel() {
    private val _checkInResponse = MediatorLiveData<Resource<BaseResponse>>
    val checkInResponse: LiveData<Resource<BaseResponse>> = _checkInResponse

    fun checkInCheckOut(apiKey: String, userId: Int, status: String, latitude: Double, longitude: Double, checkedOn: Long) {
        val data = repository.checkInCheckOutUser(apiKey,userId,status,latitude,longitude,checkedOn)
        _checkInResponse.addSource(data) {
            if (it is Resource.Success || it is Resource.Failure)
                _checkInResponse.removeSource(data)
            _checkInResponse.value = it
        }
    }
}

此代码假设 data 只发出一个终端元素 Resource.Success 或 Resource.Failure 并用它清理源代码。

【讨论】:

  • 您好,感谢您的回答。您的答案看起来像是不需要创建数据类的最干净的方法。所以我接受它作为答案。
【解决方案2】:

您想要的方法是使用一个中间的LiveData 来保存请求参数,命名为queryLiveData。当checkInCheckOut 函数被调用时,我们为其设置了一个新值,这会导致checkInResponse 发生变化。然后更改将使用switchMap 转换为repository.checkInCheckOutUser 的结果。

CheckInMapViewModel:

class CheckInMapViewModel : ViewModel() {

    private val queryLiveData = MutableLiveData<CheckInCheckOutParam?>()

    init {
        queryLiveData.postValue(null)
    }

    val checkInResponse: LiveData<Resource<BaseResponse>> =
        queryLiveData.switchMap { query ->
            if(query == null) {
                AbsentLiveData.create()
            } else {
                repository.checkInCheckOutUser(
                    query.apiKey,
                    query.userId,
                    query.status,
                    query.latitude,
                    query.longitude,
                    query.checkedOn
                )
            }
        }

    fun checkInCheckOut(
        apiKey: String,
        userId: Int,
        status: String,
        latitude: Double,
        longitude: Double,
        checkedOn: Long
    ) {
        queryLiveData.postValue(
            CheckInCheckOutParam(apiKey, userId, status, latitude, longitude, checkedOn)
        )
    }

    private data class CheckInCheckOutParam(
        val apiKey: String,
        val userId: Int,
        val status: String,
        val latitude: Double,
        val longitude: Double,
        val checkedOn: Long
    )
}

AbsentLiveData:

/**
 * A LiveData class that has `null` value.
 */
class AbsentLiveData<T : Any?> private constructor(resource: Resource<T>) :
    LiveData<Resource<T>>() {

    init {
        // use post instead of set since this can be created on any thread
        postValue(resource)
    }

    companion object {

        fun <T> create(): LiveData<Resource<T>> {
            return AbsentLiveData(Resource.empty())
        }
    }
}

【讨论】:

  • @SomeshKumar:此代码基于架构组件的官方 android 示例。请查看this 以查看几乎类似的案例。
  • 为此有很多代码实验室。使用 android studio 3.5 并导航到 File->new-> Import Sampe 并尝试导入 sunflower 应用程序或任何应用程序。然后,您可以获得整个代码库的帮助
  • @aminography 这正是我现在正在做的事情。为GET 请求创建数据类(我不知道为什么,但这对我来说感觉不对。)。在进一步评论之前,我将等待更多答案。谢谢。
  • 使用queryLiveData和switchMap到需要的LiveData的概念是答案的重点。好的。
【解决方案3】:

试试这个:

class CheckInMapViewModel : ViewModel() {
    private val _checkInResponse: MediatorLiveData<Resource<BaseResponse>> = MediatorLiveData()
    val checkInResponse: LiveData<Resource<BaseResponse>>
    get() = _checkInResponse

    init {
        _checkInResponse.addSource(checkInCheckOut()) {
            _checkInResponse.value = it
        }
    }

    fun checkInCheckOut(apiKey: String, userId: Int, status: String, latitude: Double, longitude: Double, checkedOn: Long): LiveData<Resource<BaseResponse>> {
        return repository.checkInCheckOutUser(apiKey,userId,status,latitude,longitude,checkedOn)
    }
}

【讨论】:

  • 嘿,感谢您的回复,但我想知道如何在ViewModel 的init {} 块内通过所需的纬度,在获得用户位置之后很久,我得到了纬度。
  • @SomeshKumar 你从哪里得到用户的位置?添加更多片段和视图模型的代码可能有助于消除这种情况
  • 用户的位置在片段内。片段包含一个将调用 API 的按钮。我想相关代码已经贴出来了。
  • @SomeshKumar 你可以直接从你的activity或者fragment中开始观察checkInCheckOut函数
  • @p.mathew13 这会给我我正在观察的相同的实时数据实例吗?或任何其他片段正在观察?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-05-10
  • 1970-01-01
  • 2020-03-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多