#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
синтаксис для функции.