Android Coroutine Использование в длительных работах

Я хочу спросить о моей реализации Coroutine, я не могу передать здесь код, но я объясню его. У меня есть независимый загрузчик класса. Он отвечает за проверку соединения, загрузку файла и распаковку файла. Но если пользователь хочет выйти из этого действия, задание должно быть отменено.
Скачать.kt

lateinit var job : Job
fun getZip(interface){
   job = CoroutineScope(Dispatcher.IO).launch{
       val connection = async { connect() }.await()
       if (connection == ok){
           launch {
               createFolders()
               downloadZip(this).await()  //pass scope here, I have  to put await because unpack throw null on  downloaded file
               unpackZip()
            }
        }
   }
}
    private suspend fun connect(): HttpsURLConnection
    =  withContext(Dispatchers.IO) {
         //code
    }

fun downloadZip(scope: CoroutineScope ) : Deferred<Unit>
    = scope.async {
            while(lenl != -1){
                ensureActive()
                //code
            }
      }

viewModel.kt

fun stopJob(){
    downloader.job.cancel()
}

Это качественная реализация сопрограммы? Мне очень не нравится downloadZip(this).await() в разделе запуска, но на данный момент это работает.

Я реализую всю сопрограмму, но не знаю, хороший ли это способ

//Редактировать №1 Что вы думаете, ребята, об этом?

if (connection == ok){
           launch(this.coroutineContext) {
               createFolders()
               downloadZip(this)
            }.join()

            launch(this.coroutineContext)  {
               unpackZip()
            }.join
        }

и в функции распаковки я удаляю возврат и меняю scope.async на scope.launch

0
0
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Что вы делаете неправильно:

  • Использование lateinit для вашей работы. Нет причин использовать lateinit вместо Job? со значением по умолчанию null. Вы подвергаете себя ошибкам NPE без всякой пользы.

  • Использование CoroutineScope вместо viewModelScope. Это предотвращает автоматическую отмену задания, когда пользователь покидает действие. Вы упомянули, что хотите такого поведения. Если вы действительно хотите, чтобы задание продолжалось после закрытия Activity, было бы более целесообразно использовать WorkManager или Service, чтобы гарантировать фактическое завершение работы.

  • Использование async { ... }.await(). Нет никаких причин делать это, и компилятор выдает вам предупреждение, если вы это сделаете. Если вы сразу ожидаете результата другой сопрограммы, то вам не нужно было запускать ее в первую очередь, потому что вы хотели, чтобы результат был синхронным, а не асинхронным. withContext(Dispatchers.IO) { ... } следует использовать вместо async/await, но только если вы вызываете блокирующую функцию. Ваша функция connect является функцией приостановки, а не функцией блокировки, поэтому вам не нужно этого делать.

  • Точно так же launch { ... }.join(), как показано в вашем дополнительном образце, столь же бессмысленно.

  • Запуск внутренней дочерней сопрограммы без причины. Это дополнительный уровень свертки, который затруднит отмену вашей функции.

  • Запускаем новую сопрограмму в downloadZip(). Поскольку вы запускаете новую сопрограмму, не имеющую отношения к той, из которой вы ее запустили, ее нельзя отменить вручную, если только это не та сопрограмма, которую вы сохраняете в свойстве Job, которое вы можете напрямую отменить.

Я бы превратил функцию getZip и downloadZip в функции приостановки. Затем ViewModel может сохранить свой собственный экземпляр Job и отменить его напрямую. Поскольку задание можно запустить в viewModelScope, вы получаете поведение автоматической отмены, когда пользователь покидает действие.

Пример:

class Download {

    suspend fun getZip(interface){
        val connection = connect()
        if (connection == ok){
            createFolders()
            downloadZip()
            unpackZip()
         } else {
             // Probably should return some result for failure so
             // view model can decide what to do, such as showing error in UI.
         }
    }

    private suspend fun connect(): HttpsURLConnection
    =  withContext(Dispatchers.IO) {
         //code
    }

    fun downloadZip() {
        while(lenl != -1){
            yield()
            //code
        }
    }
}
// In ViewModel:

private var downloadJob: Job? = null

fun startDownload() {
    downloadJob ?: return // ignore request, already started download
    downloadJob = viewModelScope.launch {
        downloader.getZip(xxxxx)
    }
}

fun cacnelDownload() {
    downloadJob?.cancel()
    downloadJob = null
}

Другие вопросы по теме