Создание настраиваемого сетевого списка для Retrofit Kotlin Coroutine - PullRequest
1 голос
/ 28 апреля 2020

Я использую сервис ретрофита без обратного вызова. Таким образом, это, вероятно, происходит с Kotlin Coroutine suspend fun. Я упомянул много блогов, средств массовой информации и так много учебников. Ну, это легко получить ответ, используя сопрограмму scope с IO and Main потоком.

Итак, после обращения к некоторым примерам я собираюсь сделать код, подобный этому:

Restrofit Service Interface RetrofitInterfaces.kt:

interface RetrofitInterfaces {
    @FormUrlEncoded
    @POST("get-videos-pagination")
    suspend fun getTemplates(@Field("app_name") app_name: String,
                             @Field("sort_by") sortBy: String,
                             @Field("video_loaded_ids") loadedIds: String): Response<Model_Video_List>
}

Где Model_Video_List.kt class - это класс данных моего ответа.

Retrofit Builder RetrofitClient.kt:

object RetrofitClient {
    fun makeRetrofitService(mContext: Context): RetrofitInterfaces {
    val gson = GsonBuilder().setLenient().create()
    return Retrofit.Builder()
            .baseUrl(AppPreferences.getBaseUrl(mContext) + "/api/v3/")
            .addConverterFactory(GsonConverterFactory.create(gson))
            .build().create(RetrofitInterfaces::class.java)
}
}

Здесь я использую Gson на serialise или deserialise Json data.

Теперь, что является проблемой c и где возникла проблема, чтобы получить ответ об ошибке Я использую ResultWrapper.kt:

class ResultWrapper {
    enum class ErrorCode {
        NETWORK,// Connection failed or timeout
        SERVER,// Server errors due to maintenance or over request
        INVALID// Response is success but can't serialised with Data Class!
    }

    interface ResponseWrapper {
        suspend fun onSuccess(response: Response<Any>)

        suspend fun onFailure(eCode: ErrorCode)
    }

    private var responseWrapper: ResponseWrapper? = null
        set

    suspend fun enqueue(response: Response<Any>, responseWrapper: ResponseWrapper) {
        this.responseWrapper = responseWrapper
        if (response.isSuccessful) {
            responseWrapper.onSuccess(response)
        } else {
            val errorCode = response.code()
            when (errorCode) {
                in 200..299 -> {
                    responseWrapper.onFailure(ErrorCode.INVALID)
                }
                in 400..499 -> {
                    responseWrapper.onFailure(ErrorCode.NETWORK)
                }
                in 500..599 -> {
                    responseWrapper.onFailure(ErrorCode.SERVER)
                }
            }
        }
    }
}

Итак, теперь я могу назвать это enqueue fun из любого места в app как:

private fun getVideoList(loadedIds: String, sortBy: String) {
    val service = RetrofitClient.makeRetrofitService(mContext)
    scope.launch {
        val response = service.getTemplates("MyApp", sortBy, loadedIds)
        val resultWrapper = ResultWrapper()
        resultWrapper.enqueue(response, object : ResultWrapper.ResponseWrapper {
            override suspend fun onSuccess(response: Response<Any>) {

            }

            override suspend fun onFailure(eCode: ResultWrapper.ErrorCode) {

            }
        })
    }
}

Но я получаю compile error для передачи response в resultWrapper.enqueue(), потому что в ResultWrapper.ResponseWrapper interface есть метод, определенный с suspend fun onSuccess(response: Response<Any>). Таким образом, здесь возникла проблема с преобразованием типов!

Если Retrofit CallBack<T> может обрабатывать любой тип Model or Data classes и возвращать, который является точным tpyed. Как и в случае с вышеприведенным сервисом, в ответ требуется класс Model_Video_List, а для других сервисов требуется собственный класс, что делать для этого? Есть ли единственный способ использовать отдельную функцию для каждого сервиса? Или это возможно только в Java?!

1 Ответ

2 голосов
/ 28 апреля 2020

Вы можете ввести обобщенный c тип параметра T в своем классе ResultWrapper, поэтому при создании экземпляра этого класса необходимо указывать фактический тип вместо T.

* 1005. * Измените ResultWrapper на это:
class ResultWrapper<T> {
    enum class ErrorCode {
        NETWORK,// Connection failed or timeout
        SERVER,// Server errors due to maintenance or over request
        INVALID// Response is success but can't serialised with Data Class!
    }

    interface ResponseWrapper<T> {
        suspend fun onSuccess(response: Response<T>)

        suspend fun onFailure(eCode: ErrorCode)
    }

    private var responseWrapper: ResponseWrapper<T>? = null
        set

    suspend fun enqueue(response: Response<T>, responseWrapper: ResponseWrapper<T>) {
        this.responseWrapper = responseWrapper
        if (response.isSuccessful) {
            responseWrapper.onSuccess(response)
        } else {
            val errorCode = response.code()
            when (errorCode) {
                in 200..299 -> {
                    responseWrapper.onFailure(ErrorCode.INVALID)
                }
                in 400..499 -> {
                    responseWrapper.onFailure(ErrorCode.NETWORK)
                }
                in 500..599 -> {
                    responseWrapper.onFailure(ErrorCode.SERVER)
                }
            }
        }
    }
}

А также измените свой метод на это:

private fun getVideoList(loadedIds: String, sortBy: String) {
    val service = RetrofitClient.makeRetrofitService(mContext)
    scope.launch {
        val response = service.getTemplates("MyApp", sortBy, loadedIds)
        val resultWrapper = ResultWrapper<Model_Video_List>()
        resultWrapper.enqueue(response, object : ResultWrapper.ResponseWrapper<Model_Video_List> {
            override suspend fun onSuccess(response: Response<Model_Video_List>) {

            }

            override suspend fun onFailure(eCode: ResultWrapper.ErrorCode) {

            }
        })
    }
}
...