«Неподходящий вызов метода блокировки» — как обработать это предупреждение в Android Studio

#android #kotlin #inputstream #kotlin-coroutines #outputstream

#Android #kotlin #входной поток #котлин-сопрограммы #outputstream

Вопрос:

Я написал этот фрагмент кода для загрузки файлов изображений из хранилища firebase в локальное хранилище.

 contentResolver.openOutputStream(uri)?.use { ops ->   // *
    Firebase.storage.getReferenceFromUrl(model.mediaUrl).stream.await().stream.use { ips ->
        val buffer = ByteArray(1024)
        while (true) {
            val bytes = ips.read(buffer)   // *
            if (bytes == -1)
                break
            ops.write(buffer, 0, bytes)   // *
        }
    }
}
 

В отмеченных строках android studio выдает мне Inappropriate blocking method call предупреждение, выделяя функции openOutputStream(), read() и write(). Я запускал код несколько раз, и он работал правильно. Весь этот фрагмент кода находится внутри функции приостановки, вызываемой из сопрограммы ввода-вывода.
Кто-нибудь, пожалуйста, подскажите мне действительную причину и способы устранения этого предупреждения.

Редактировать Этот код вызывается в следующем контексте.

 fun someFunc() {
    lifecycleScope.launch(IO) {
        val uri = getUri(model)
        ...
    }
    ...
}
...
suspend fun getUri(model: Message): Uri {
    ... // Retrive the image file using mediastore.
    if ( imageNotFound ) return downloadImage(model)
    else return foundUri
}
suspend fun downloadImage(model: Message): Uri {
    ... // Create contentvalues for new image.
    val uri = contentResolver.insert(collectionUri, values)!!
    // Above mentioned code snippet is here.
    return uri
}
 

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

1. Пожалуйста, покажите больше контекста этого кода, чтобы мы могли видеть, как он вызывается в диспетчере ввода-вывода. Кстати, нет никаких оснований призывать close() к цели use .

2. @Tenfour04 Означает больше контекста? Вы хотите, чтобы я отредактировал сообщение, чтобы добавить код для вызова этой функции.

3. Да, код вокруг этого кода или который вызывает этот код. Или вся функция приостановки.

4. @Tenfour04 только что добавил контекст исходного кода. Посмотрите теперь, помогает ли это.

Ответ №1:

Правильно составленная функция приостановки никогда не блокируется. Он не должен зависеть от того, вызывается ли он из определенного диспетчера, а скорее должен явно переносить блокирующий код в соответствующий диспетчер.

Таким образом , части вашей функции приостановки , которые вызывают блокирующий код , должны быть обернуты withContext(Dispatchers.IO){} . Тогда сопрограмме, которая его вызывает, даже не нужно указывать диспетчера. Это делает его очень удобным lifecycleScope для вызова функций и обновления пользовательского интерфейса и позволяет не беспокоиться о диспетчерах.

Пример:

 suspend fun foo(file: File) {
    val lines: List<String> = withContext(Dispatchers.iO) {
        file.readLines()
    }
    println("The file has ${lines.size} lines.")
}
 

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

1. Хорошо.. Но если функция suspend должна что-то возвращать, то все тело функции не может быть обернуто с помощью withContext{} . В этом случае узел вызова должен принимать решения о вызове этой функции с соответствующим диспетчером.

2. Это неправда. withContext возвращает результат.

3. Ах да.. Это работает.. Спасибо. 🙂 Но это делает код более вложенным!

4. В большинстве случаев на сайте вызова меньше вложенности, потому что вам не придется менять диспетчеров взад и вперед. Для приостановленных функций, которые не требуют нескольких диспетчеров, вложенность отсутствует, поскольку вы можете использовать = withContext синтаксис для функции.