首页 > 解决方案 > 带有协程和流的 Android 通用 Gson.fromJson() 转换

问题描述

我正在制作用于点击 otp api 的通用类。任何人都可以使用 otp 部分只需传递请求、响应类和 url,所有这些都将由这个 otp 部分完成。请注意:此响应类可以是不同的类型(例如:MobileOtpResponse、EmailOtpResponse)

下面是通用 OtpClient,它接受任何请求类型并返回特定传递的 ResponseType(例如:传递的请求类是 OtpRequest,传递的响应类型类是 OtpResponse)

 interface OtpClient {
  @POST
  suspend fun <Request : Any, ResponseType> sendOtp(@Url url: String,
   @Body request:@JvmSuppressWildcards Any): @JvmSuppressWildcards ResponseType
 }

OTP请求

data class OtpRequest(@SerializedName("mobile_number") val mobileNumber: String,@SerializedName("app_version") val appVersion: String)

响应

data class OtpResponse(@SerializedName("status") val status: String = "",
                       @SerializedName("response") val response: OtpData? = null)
data class OtpData(
        @SerializedName("otp_status") val otpStatus: Boolean = false,
        @SerializedName("message") val message: String = "",
        @SerializedName("error") val error: Int? = null,
        @SerializedName("otp_length") val otpLength: Int? = null,
        @SerializedName("retry_left") val retryLeft: Int? = null,)

现在我创建 Repo 来调用这个 api 这只是使用 flow 并且当数据获取时它会发出数据


class OtpRepoImpl<out Client : OtpClient>(val client: Client) :OtpRepo  {
    override fun <Request:Any, ResponseType> sentOtpApi(url: String, request: Request): Flow<ResponseType> {

        return flow<ResponseType> {
            // exectute API call and map to UI object

            val otpResponse = client.sendOtp<Request, ResponseType>(url,request)
            emit(otpResponse)
        }.flowOn(Dispatchers.IO) // Use the IO thread for this Flow
    }


}

此 repo 用于 viewmodel 类

 @ExperimentalCoroutinesApi
    fun <A : Class<ResponseType>, Request : Any, ResponseType : Any> sendOtp(a: Class<ResponseType>, request: Request, response: ResponseType, url: String) {
        viewModelScope.launch {
            repo.sentOtpApi<Request, ResponseType>(url, request = request)
                    .onStart { _uiState.value = OtpState.Loading(true) }
                    .catch { cause ->
                        _uiState.value = OtpState.Loading(false)
                        getResponseFromError<Class<ResponseType>,ResponseType>(cause, response) {
//                            emit(it)
                        }
                    }
                    .collect {
                        _uiState.value = OtpState.Loading(false)
                        _uiState.value = OtpState.Success(it)
                    }
        }
    }

正如你在上面看到的,这个 sendOtp 方法是从视图类调用的,在这个方法中,我们使用 repo.sentOtpApi 并传递通用请求响应类型。我在 catch 块中获取数据,因为 api 是在 400 HttpException 中发送错误 otp 数据,所以我创建了另一个获取错误响应的方法getResponseFromError它应该解析 errorBody 响应并调用此 lambda 块。

 private suspend fun <A : Class<*>, ResponseType : Any> getResponseFromError( cause: Throwable, rp: ResponseType, block: suspend (ResponseType) -> Unit) {

        if (cause is HttpException) {
            val response = cause.response()

            if (response?.code() == 400) {
                println("fetching error Response")
                val errorResponse = response.errorBody()?.charStream()
                val turnsType = object : TypeToken<ResponseType>() {}.type
                val finalErrorResponse = Gson().fromJson<ResponseType>(errorResponse, turnsType)
               block(finalErrorResponse)
            } else {
                println("someOther exception")
            }
        } else
            _uiState.value = OtpState.Error(cause)
    }

所以在这里我面临上述方法中的问题

    val turnsType = object : TypeToken<ResponseType>() {}.type
    val finalErrorResponse = Gson().fromJson<ResponseType>(errorResponse, turnsType)
    block(finalErrorResponse)

这个 finalErrorResponse 正在返回 LinkedTreeMap 而不是ResponseType(在这种情况下是 OtpResponse)我也尝试过像这样使用 Class<*> 类型

  val turnsType = object : TypeToken<A>() {}.type
  val finalErrorResponse = Gson().fromJson<A>(errorResponse, turnsType)

但它不工作。

这个 sendOtp viewmodel 函数的调用就像

   var classType = OtpResponse::class.java
   otpViewModel.sendOtp(a = classType, request = otpRequest, response = OtpResponse() , url = 
   "http://preprod-api.nykaa.com/user/otp/v2/send-wallet-otp")

[![finalErroResponse 中的值][1]][1] [1]: https://i.stack.imgur.com/Holui.png

必需:finalErroResponse 应该是 OtpResponse 类型,因为它是在 sentOtp 函数中传递的

请帮忙 :)

标签: androidgenericsgsoncoroutineflow

解决方案


推荐阅读