Pull to refresh

Comments 10

А так как все равно все заканчивается обновлением MutableLiveData, то используя AsyncTask или даже просто viewModelJob = AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> liveData.postValue(download())); можно еще много кода выкинуть ;-)
Есть один нюанс, вы предлагаете для отмены корутин использовать просто
viewModelJob.cancel()
но это подходит только если вы больше не планируете запускать на этом Job других корутин. Иначе, если планируете дальнейшее использование CoroutineScope, нужно использовать
viewModelJob.cancelChildren()

Можете провести
небольшой эксперимент
import kotlinx.coroutines.*

class CoroutinesCancelationClass {
    private val job = Job()
    private val scope = CoroutineScope(Dispatchers.IO + job)


    fun doWork(tag: String) {
        scope.launch{
            println("start $tag")
            delay(1000)
            println("end $tag")
        }
    }

    fun cancel(){
        job.cancel()
    }

    fun cancelChildren() {
        job.cancelChildren()
    }
}

fun testCancelChildren() {
    val testObj = CoroutinesCancelationClass()

    testObj.doWork("testCancelChildren 1")
    testObj.cancelChildren()
    testObj.doWork("testCancelChildren 2")

}

fun testCancel() {
    val testObj = CoroutinesCancelationClass()

    testObj.doWork("testCancel 1")
    testObj.cancel()
    testObj.doWork("testCancel 2")

}

fun main(args: Array<String>) = runBlocking {
    testCancel()
    delay(2000)
    testCancelChildren()
    delay(2000)
}


Вывод будет следующий:
start testCancel 1
start testCancelChildren 1
start testCancelChildren 2
end testCancelChildren 2

Т.е. в случае вызова просто cancel вторая корутина даже не запустилась, в случае вызова cancelChildren вторая корутина запустилась и успешно выполнилась.
но это подходит только если вы больше не планируете запускать на этом Job других корутин.

В моем примере .cancel() вызывается в onDestroy() фрагмента. Но для общего случая, замечание верное, добавил информацию в статью.

 @Override
    protected void onDestroy() {
        super.onDestroy();

        if (mViewModelStore != null && !isChangingConfigurations()) {
           mViewModelStore.clear();
        }

        mFragments.dispatchDestroy();
    }
Спасибо за статью. Я столкнулся с проблемой, что если даже закэнселить джоб, то реквест, который ретрофит отправил не кэнселится. Тривиального решения данной проблемы я не нашел и отложил использования корутин до того, как это github.com/square/retrofit/pull/2886 не будет доступно в релизе.
Если соединение нестабильно или отсутствует, Retrofit может выбросить исключение по тайм-ауту и уронить приложение. Как вы обходите эту проблему?
В текущей реализации был использован вариант с обработкой этого исключения на уровне repository и дальнейшего преобразования в общий вид ошибки API.
Однако после вашего комментария я задумался как бы почистить код и перенес отлов exception в BaseViewModel, а так же добавил дефолтное отображение ошибки через SingleLiveEvent(могу рассказать подробнее, если нужно)

Примерный вид:
  private var viewModelJob = SupervisorJob()

  private inline fun <P> doCoroutineWork(
        crossinline doOnAsyncBlock: suspend CoroutineScope.() -> P,
        coroutineScope: CoroutineScope,
        context: CoroutineContext
    ) {
        coroutineScope.launch {
            withContext(context) {
                try {
                    doOnAsyncBlock.invoke(this)
                } catch (e: UnknownHostException) {
                    e.printStackTrace()
                    Log.d(TAG, "Server is unreachable")
                } catch (e: SocketTimeoutException) {
                    e.printStackTrace()
                    Log.d(TAG, "No internet connection")
                }
            }
        }
    }
Спасибо! Я тоже использую try-catch, но сначала в методе onOnIntercept() интерфейса okhttp3.Interceptor для обработки ошибочных JSON, а затем в каждом запросе для обработки прочих ошибок (ошибок соединения). Но ваш опыт тоже интересен.
Думаю, следует добавить ещё одно исключение: SSLHandshakeException. Это когда у сервера нет сертификата.
Столкнулся со следующей проблемой. Краш-логи в релизном приложении очень сложно понять. Строки ссылаются не на те, что в коде, декомпилировать в Java нормально нельзя. Также порядок вызова методов очень странный. Найти ошибку становится почти нереально. Это не корутины вносят сумятицу?
Вот сколько статей не прочитал, нигде нет примера с implementation by delegation для CoroutineScope. Мне кажется, лучше использовать его, если нет какой-то необходимости хранить экземпляр Job отдельно.

class MyViewMode : BaseViewMode(), CoroutineScope by MainScope() {
   fun onDestroy() {
        cancel() // метод CoroutineScope
        // or 
        coroutineContext[Job]?.cancelChildren()
    }


    fun showSomeData() = launch {
        draw(data)
    }
}
Sign up to leave a comment.

Articles