Загрузка файлов с помощью gRP C на Android - PullRequest
1 голос
/ 15 февраля 2020

В настоящее время у меня есть сервер gRP C, который отправляет фрагменты видеофайла. Мое приложение android, написанное на Kotlin, использует сопрограммы для обновления пользовательского интерфейса (в Dispatchers.MAIN) и для обработки однонаправленного потока чанков (в Dispatchers.IO). Например:

GlobalScope.launch(Dispatchers.Main) {
   viewModel.downloadUpdated().accept(DOWNLOAD_STATE.DOWNLOADING) // MAKE PROGRESS BAR VISIBLE

      GlobalScope.launch(Dispatchers.IO) {
         stub.downloadVideo(request).forEach {
                file.appendBytes(
                    it.data.toByteArray()
                )
            }
      }.join()
      viewModel.downloadUpdated().accept(DOWNLOAD_STATE.FINISHED) // MAKE PROGRESS BAR DISAPPEAR
   } catch (exception: Exception) {
      viewModel.downloadUpdated().accept(DOWNLOAD_STATE.ERROR) // MAKE PROGRESS BAR DISAPPEAR
      screenNavigator.showError(exception) // SHOW DIALOG
   }
}

Это работает довольно хорошо, но мне интересно, нет ли более чистого способа обработки загрузок. Я уже знаю о DownloadManager, но мне кажется, что он принимает только HTTP-запросы, и поэтому я не могу использовать мою заглушку gRP C (я могу ошибаться, пожалуйста, сообщите мне, если так). Я также проверил WorkManager, и вот та же проблема, я не знаю, является ли это правильным способом обработки этого случая.

Итак, здесь есть два вопроса:

  • Есть ли способ обрабатывать запросы gRP C в чистом виде, что означает, что я могу теперь, когда он запускается, заканчивается, не удается и что я могу отменить должным образом?
  • Если нет, есть ли лучший способ использовать сопрограммы для этого?

РЕДАКТИРОВАТЬ

Для тех, кто заинтересован, я думаю, что я придумал фиктивный алгоритм загрузки при обновлении индикатора выполнения (открытый для улучшений):

suspend fun downloadVideo(callback: suspend (currentBytesRead: Int) -> Unit) {
   println("download")

   stub.downloadVideo(request).forEach {
      val data = it.data.toByteArray()

      file.appendBytes(data)
      callback(x) // Where x is the percentage of download
   }

    println("downloaded")
}

class Fragment : CoroutineScope { //NOTE: The scope is the current Fragment
    private val job = Job()

    override val coroutineContext: CoroutineContext
        get() = job

    fun onCancel() {
        if (job.isActive) {
            job.cancel()
        }
    }

    private suspend fun updateLoadingBar(currentBytesRead: Int) {
        println(currentBytesRead)
    }

    fun onDownload() {

        launch(Dispatchers.IO) {
            downloadVideo { currentBytes ->
                withContext(Dispatchers.Main) {

                    updateLoadingBar(currentBytes)

                    if (job.isCancelled)
                        println("cancelled !")
                }
            }
        }
    }
}

Для получения дополнительной информации, пожалуйста, проверьте: Введение в сопрограммы

РЕДАКТИРОВАТЬ 2

Как предложено в комментариях, мы могли бы фактически использовать потоки для обработки этого, и это будет дать что-то вроде:

suspend fun foo(): Flow<Int> = flow { 
   println("download")
   stub.downloadVideo(request).forEach {
      val data = it.data.toByteArray()

      file.appendBytes(data)
      emit(x) // Where x is the percentage of download
   }
   println("downloaded")
}

class Fragment : CoroutineScope {
    private val job = Job()

    override val coroutineContext: CoroutineContext
        get() = job

    fun onCancel() {
        if (job.isActive) {
            job.cancel()
        }
    }

    private suspend fun updateLoadingBar(currentBytesRead: Int) {
        println(currentBytesRead)
    }

    fun onDownload() {

        launch(Dispatchers.IO) {
            withContext(Dispatchers.Main) {
                foo()
                    .onCompletion { cause -> println("Flow completed with $cause") }
                    .catch { e -> println("Caught $e") }
                    .collect { current -> 
                        if (job.isCancelled)
                            return@collect

                        updateLoadingBar(current)
                }
            }
        }
    }
}

1 Ответ

0 голосов
/ 15 февраля 2020

gRP C может быть много вещей, поэтому в этом отношении ваш вопрос неясен. Наиболее важно, что он может быть полностью асинхронным c и основан на обратном вызове, что означает, что его можно превратить в Flow, который вы можете собирать в главном потоке. Однако запись в файл блокируется.

Ваш код, похоже, сразу же отправляет сигнал FINISHED, как только он запускает загрузку в фоновом режиме. Вам, вероятно, следует заменить launch(IO) на withContext(IO).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...