Ошибка Suspend functions cannot be made Composable при попытке скачать файл

Рейтинг: 0Ответов: 2Опубликовано: 01.05.2023

При попытке скачать файл, получаю ошибку:

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

Ответы

▲ 0

Общение 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}")
}
  1. Получение файла занимает время, сначала на экран будет выведено Файл: null. Как только файл будет получен, текст автоматически обновится.

  2. Не создавайте retrofit в контексте Composable-функции, так как объект будет создаваться каждый раз при рекомпозиции Composable-функции и это может повлиять на производительность. Создайте объект retrofit один раз в контексте Application и далее пользуйтесь этим объектом.

  3. Не забудьте добавить следующий импорт, чтобы заработала конструкция by:

    import androidx.compose.runtime.getValue
    
  4. Посмотрите в документации какие еще бывают side-эффекты в мире Compose.

▲ 0

Исходя из приведенного кода, вам не нужен контекст, а нужна лишь директория cacheDir.

Поэтому, чтобы исправить эту ошибку, уберите аннотацию @Composable и передайте в функцию cacheDir.

В таком случае функцию следует назвать с маленькой буквы и желательно придумать более осмысленное название для функции:

suspend fun downloadBookOrNull(cacheDir: File): File? {
    ...
}

Когда вы определитесь, как именно будет использоваться эта функция в вашем коде, то вам поднадобится другой ответ.