Модифицированный response.errorBody.string() выдает мне предупреждение о недопустимом вызове метода блокировки в функции приостановки

#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)
}
  

Итак, у меня есть несколько вопросов об этом предупреждении и сопрограммировании

  1. Почему последний не выдает мне предупреждение, но все остальные области сопрограммы?
 GlobalScope.launch(Dispatchers.IO) {
    Gson().fromJson(response.errorBody()?.string(), ExceptionResponse::class.java)
}
  

  1. Похоже, мне нужен структурированный параллелизм, потому что мне нужно проанализировать JSON и вернуть его. Если я CoroutineScope или GlobalScope тогда я верну null, если я не использую scope.join . Тогда не следует ли мне использовать CoroutineScope(), который использует область сопрограммы родительского / вызывающего объекта для структурированного параллелизма?

  1. Похоже, что 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);
    }
  }
  

  1. Можно ли использовать последний, потому что тогда я создаю GlobalScope в CoroutineScope.

  1. использует ли 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. большое вам спасибо за ссылку и ваши ответы