Ошибка Suspend functions cannot be made Composable при попытке скачать файл
При попытке скачать файл, получаю ошибку:
Suspend functions cannot be made Composable
Как можно поправить?
@Composable
suspend fun CheckVerMain(): File? {
val context = LocalContext.current
val retrofit = RetrofitProvider.getInstance().create(CheckVerService::class.java)
return try {
val requestBody = retrofit.downloadFile()
val file = requestBody.byteStream().toContent(context)
file
} catch (e: Exception) {
Log.d("res", "ERROR DOWNLOAD = ${e.printStackTrace()}")
null
}
}
private fun InputStream.toContent(context: Context): File {
use {
val file = File(context.cacheDir, "book.epub")
FileOutputStream(file).use { output ->
val buffer = ByteArray(4 * 1024)
var read: Int
while (read(buffer).also { read = it } != -1) {
output.write(buffer, 0, read)
}
output.flush()
}
return file
}
}
Ответы (2 шт):
Общение Composable-функции с внешим миром это side-эффект.
Вызывать suspend-функцию из Composable-функции конечно же нельзя. Рекомпозиция (вызов Composable-функции) может происходить ~100 раз в секунду, столько раз была бы вызвана функция downloadFile(), если бы код удалось скомпилировать.
Чтобы подружить suspend и Composable, нужно сформировать состояние (стейт). Создать Compose-стейт на основе suspend-функции можно с помощью produceState:
@Composable
fun FileContent() {
val context = LocalContext.current
val retrofit = (context as App).retrofit
val file by produceState(initialValue = null as File?) {
value = retrofit.downloadFile()
}
Text("Файл: ${file?.name}")
}
Получение файла занимает время, сначала на экран будет выведено
Файл: null. Как только файл будет получен, текст автоматически обновится.Не создавайте
retrofitв контексте Composable-функции, так как объект будет создаваться каждый раз при рекомпозиции Composable-функции и это может повлиять на производительность. Создайте объектretrofitодин раз в контексте Application и далее пользуйтесь этим объектом.Не забудьте добавить следующий импорт, чтобы заработала конструкция
by:import androidx.compose.runtime.getValueПосмотрите в документации какие еще бывают side-эффекты в мире Compose.
Исходя из приведенного кода, вам не нужен контекст, а нужна лишь директория cacheDir.
Поэтому, чтобы исправить эту ошибку, уберите аннотацию @Composable и передайте в функцию cacheDir.
В таком случае функцию следует назвать с маленькой буквы и желательно придумать более осмысленное название для функции:
suspend fun downloadBookOrNull(cacheDir: File): File? {
...
}
Когда вы определитесь, как именно будет использоваться эта функция в вашем коде, то вам поднадобится другой ответ.