#android #api #kotlin #retrofit2 #coroutine
#Android #API #kotlin #retrofit2 #сопрограмма
Вопрос:
Я разрабатываю приложение для Android.
Я использую Retrofit2 и сопрограмму для извлечения некоторых данных из моего Rest API.
Когда в моем Rest API возникает исключение, оно возвращает код исключения и сообщение об исключении с HTTP status = 4xx или 5xx, как показано на скриншоте ниже
Если ответ является исключением, я сопоставляю код исключения и ExceptionMessage с ответом в моем приложении для Android в соответствии с приведенным ниже кодом.
val exceptionBody = Gson().fromJson(response.errorBody()?.string(), ExceptionResponse::class.java)
Это ExceptionResponse
класс
data class ExceptionResponse(
val exceptionCode: String,
val exceptionMessage: String
)
Вот проблема. Когда я это делаю response.errorBody()?.string()
, Android Studio выдает мне предупреждение о «неправильном вызове метода блокировки»
Вот мой repository
вызов сетевого вызова
override fun fetchData() {
CoroutineScope(Dispatchers.IO).launch {
val fetchedData = myRemoteDataSource.fetchData()
}
}
Вот MyRemoteDataSource
класс
class MyRemoteDataSourceImpl(
private val myAPIService: MyAPIService
): BaseRemoteDataSource(), MyRemoteDataSource {
override suspend fun fetchData(): Resource<List<Data>> {
return getResult {
myAPIService.fetchData()
}
}
}
И вот мой BaseRemoteDataSource
класс, который имеет getResult()
where errorBody.string
вызывается
Как вы можете видеть на скриншоте выше, единственная сопрограмма, которая не выдает мне предупреждение, является последней
GlobalScope.launch(Dispatchers.IO) {
Gson().fromJson(response.errorBody()?.string(), ExceptionResponse::class.java)
}
Итак, у меня есть несколько вопросов об этом предупреждении и сопрограммировании
- Почему последний не выдает мне предупреждение, но все остальные области сопрограммы?
GlobalScope.launch(Dispatchers.IO) {
Gson().fromJson(response.errorBody()?.string(), ExceptionResponse::class.java)
}
- Похоже, мне нужен структурированный параллелизм, потому что мне нужно проанализировать JSON и вернуть его. Если я
CoroutineScope
илиGlobalScope
тогда я верну null, если я не используюscope.join
. Тогда не следует ли мне использовать CoroutineScope(), который использует область сопрограммы родительского / вызывающего объекта для структурированного параллелизма?
- Похоже, что response.errorBody()?.string() анализирует JSON, который должен быть в диспетчерах.По умолчанию, не так ли? Просто для справки, я включаю исходный код для
string()
public final String string() throws IOException {
try (BufferedSource source = source()) {
Charset charset = Util.bomAwareCharset(source, charset());
return source.readString(charset);
}
}
- Можно ли использовать последний, потому что тогда я создаю GlobalScope в CoroutineScope.
- использует ли withContext() область сопрограммы вызывающего абонента точно так же, как CoroutineScope(), или он создает новую область с разными диспетчерами?
Извините, что сразу сбрасываю вопросы, но все они связаны. Спасибо, ребята!!!
Ответ №1:
Нормально вызывать блокировку выполнения в IO
контексте, потому что он предназначен для выполнения таких задач. Поэтому, я думаю, лучший способ — написать функцию расширения ResponseBody
для того, чтобы сделать это в соответствующем IO
контексте. Однако в этой функции также появляется предупреждение, но, поскольку мы знаем, что мы хорошо с этим справились, мы можем его подавить.
@Suppress("BlockingMethodInNonBlockingContext")
suspend fun ResponseBody.stringSuspending() =
withContext(Dispatchers.IO) { string() }
Поэтому мы уверены, что ResponseBody.string()
это будет выполнено в IO
контексте, независимо от того, что оно может быть обернуто другим контекстом:
Gson().fromJson(response.errorBody()?.stringSuspending(), ExceptionResponse::class.java)
Комментарии:
1. Большое вам спасибо за ваш ответ. Итак, вы говорите, что я могу просто проигнорировать предупреждение и выполнить .string() в текущей области сопрограммы? Затем он блокирует весь поток?
2. Пожалуйста 🙂 Предположим, что вы вызываете блок кода, содержащий
ResponseBody.stringSuspending()
вDefault
контексте. Точно так же, как:GlobalScope.launch(Dispatchers.Default) { Gson().fromJson(response.errorBody()?.stringSuspending(), ExceptionResponse::class.java) }
. Когда управление выполнением достигаетstringSuspending()
, происходит переключение контекста. Это означает, что текущий поток попытается выполнить другой код (если таковой имеется) в контекстеDefault
.3. Следствием этого является то, что поток не будет заблокирован. С другой стороны,
stringSuspending()
он будет выполняться в потоке, который должен запускатьIO
коды. Как только выполнениеstringSuspending()
завершится, произойдет другое переключение контекста, и выполнение будет продолжено вDefault
контексте.4. Разделите мое понимание сопрограммы и могу ли я задать еще несколько вопросов, пожалуйста? Есть ли какая-либо конкретная причина использовать withContext() вместо CoroutineScope(), потому что в самом начале я вызываю сетевую функцию в CoroutineScope(диспетчеры. IO) тогда я могу просто использовать CoroutineScope(), потому что мне не нужно переключаться на другой контекст?
5. большое вам спасибо за ссылку и ваши ответы