Обработка с помощью модернизации сопрограммы

#android #kotlin #retrofit

#Android #kotlin #модернизация

Вопрос:

Я учусь использовать библиотеку дооснащения для разных задач, но пока не до конца понимаю, как это работает. Основная задача — получить тело, если код ответа равен 200, повторно (все остальные коды) просто установите флаг:

 import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import okhttp3.ResponseBody
import retrofit2.Response
import retrofit2.Retrofit
import retrofit2.http.GET

interface APIService {

    @GET("/")
    suspend fun getRoot(): Response<ResponseBody>
}

...

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
      
       ...

       
      button.setOnClickListener {

           val url = editText.text.toString()  
           //  url = "https://"  "google.coN"
           val retrofit = Retrofit.Builder()
                .baseUrl(url)
                .build() 
           val service = retrofit.create(APIService::class.java)
           ...

           CoroutineScope(Dispatchers.IO).launch {
                val response  = service.getRoot()
                withContext(Dispatchers.Main) {
                    if (response.isSuccessful){
                        Ok = true // address is ok
                    } else {
                        Ok = false // this address dosnt exist
                                    }
                  ....      
                        }
                    }
              }
        }
}
 

Код работает хорошо (переделанный из какого-то примера из учебника) с хорошими ссылками, но приложение вылетает всякий раз, когда адрес указан неправильно или плохо отформатирован, для этого требуется хорошо отформатированный URL («https://» )

Как изменить код, добавить исключение и выполнить предварительный формат URL?

PS: Возможно, лучше использовать OkHttp напрямую, но я использую интеграцию библиотеки GSON с этим модифицированным кодом, удаленным для ясности

Спасибо.

Комментарии:

1. блок try catch должен помочь.

2. можете ли вы предоставить код ошибки?

Ответ №1:

Сначала создайте sealed класс для хранения результата

 sealed class ApiResult<out T : Any?>

data class Success<out T : Any?>(val data: T) : ApiResult<T>()

data class ApiError(val exception: Exception) : ApiResult<Nothing>()
 

Теперь напишите вспомогательную функцию для сопоставления okhttp ответа с ApiResult

 suspend fun <T : Any> handleApi(
    call: suspend () -> Response<T>,
    errorMessage: String = "Some errors occurred, Please try again later"
): ApiResult<T> {
    try {
        val response = call()
        if (response.isSuccessful) {
            isConnectedToNetwork = true
            response.body()?.let {
                return Success(it)
            }
        }
        response.errorBody()?.let {
            try {
                val errorString  = it.string()
                val errorObject = JSONObject(errorString)
                return ApiError(
                    RuntimeException(if(errorObject.has("message")) errorObject.getString("message") else "Error occurred, Try again Later"))
            } catch (ignored: JsonSyntaxException) {
                return ApiError(RuntimeException(errorMessage))
            }
        }
        return ApiError(RuntimeException(errorMessage))
    } catch (e: Exception) {
        if (e is IOException) {
            isConnectedToNetwork = false
        }
        return ApiError(RuntimeException(errorMessage))
    }
}
 

Наконец, используйте приведенный ниже код для доступа к результату

 CoroutineScope(Dispatchers.IO).launch {
      val result: ApiResult<ResponseBody>  = handleApi( { service.getRoot() } )
      when(result){
          is ApiResult.Success -> // result.data will give you ResponseBody
          is ApiResult.ApiError -> // result.exception will provide the error
      }
}
 

Ответ №2:

Есть несколько вещей, которые могут помочь вам в этом, это будет более эффективно:

  1. Создайте модель представления и создайте ее экземпляр в своей деятельности.
  2. В модели представления создайте метод для выполнения фоновых задач, например:

    private fun loadNetworkRequest(block: suspend () -> Unit): Job {
    return viewModelScope.launch {
    try {
    block()
    }catch (ex: Exception) {
    Toast.makeText(appContext, ex.message, Toast.LENGTH_SHORT).show()
    }
    }
    }

  3. Добавьте suspend ключевое слово для запроса в файл службы, который вы хотите выполнить с помощью этого метода.

    @GET("category/")
    suspend fun getCategories(): Response<CategoryResponseModel>

  4. Выполните запрос в модели представления, например:

    fun performRequest(callback: (Boolean) -> Unit) {
    loadNetworkRequest {
    val response = service.getRoot()
    callback.invoke(response.isSuccessful)
    }
    }

  5. Вызовите метод запроса в действии.
     button.setOnClickListener {
      ....
    
       viewModel.performRequest { response ->
    
            // ok = response
        }
    }