#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:
Есть несколько вещей, которые могут помочь вам в этом, это будет более эффективно:
- Создайте модель представления и создайте ее экземпляр в своей деятельности.
- В модели представления создайте метод для выполнения фоновых задач, например:
private fun loadNetworkRequest(block: suspend () -> Unit): Job {
return viewModelScope.launch {
try {
block()
}catch (ex: Exception) {
Toast.makeText(appContext, ex.message, Toast.LENGTH_SHORT).show()
}
}
}
- Добавьте
suspend
ключевое слово для запроса в файл службы, который вы хотите выполнить с помощью этого метода.@GET("category/")
suspend fun getCategories(): Response<CategoryResponseModel>
- Выполните запрос в модели представления, например:
fun performRequest(callback: (Boolean) -> Unit) {
loadNetworkRequest {
val response = service.getRoot()
callback.invoke(response.isSuccessful)
}
}
- Вызовите метод запроса в действии.
button.setOnClickListener { .... viewModel.performRequest { response -> // ok = response } }