Kotlin coroutine немедленно выдаст исключение, если последняя операция завершилась с исключением

Когда я пытался войти в свой сервис через дооснащение. Когда моя служба отключена, через 10 секунд после нажатия кнопки я получил SocketTimeoutException исключение. Пока все нормально, но снова я снова нажал кнопку после того, как ошибка сразу же выдала ту же ошибку. Что случилось?

interface LoginService {

    @FormUrlEncoded
    @POST("/login")
    fun login(@Field("id") id: String, @Field("pw") pw: String): Deferred<Response<User>>

}

class LoginViewModel : ViewModel() {

    private var job: Job = Job()
    private val scope: CoroutineScope = CoroutineScope(Dispatchers.Main + job)
    private val service by lazy { RetrofitApiFactory().create(LoginService::class.java) }
    private val excHandler = CoroutineExceptionHandler { _, throwable ->
        Timber.e(throwable);
    }

    fun doLogin(id: String, pw: String) {
        scope.launch(excHandler) {
            val response = service.login(id, pw).await()
            if (response.isSuccessful) {
                response.body()
                        ?.let { user -> doOnSuccess(user) }
                        ?: doOnError(InvalidUserException())
            } else doOnError(Exception())
        }
    }

    private fun CoroutineScope.doOnError(e: Throwable) {
        excHandler.handleException(coroutineContext, e)
    }

    private fun doOnSuccess(user: User) {
        ...
    }

    override fun onCleared() {
        job.cancel()
    }

}

person kibar    schedule 14.10.2018    source источник
comment
Я вижу большую проблему в этом дизайне, у него есть несколько каналов для сообщения об ошибке. Сначала есть Response, который может быть успешным или нет, затем Deferred, который может быть завершен или отменен. В хорошем дизайне вместо Deferred будет suspend fun, и он будет сигнализировать обо всех ошибках через исключения. Если вы получили ответ, значит, он успешен.   -  person Marko Topolnik    schedule 15.10.2018
comment
См. здесь для подробного объяснения философии, лежащей в основе suspend fun vs . Deferred.   -  person Marko Topolnik    schedule 15.10.2018
comment
Я не могу использовать без Deferred. Выдает ошибку: java.lang.IllegalArgumentException: Unable to create call adapter for retrofit2.Response   -  person kibar    schedule 15.10.2018
comment
Да, насколько мне известно, Retrofit поддерживает только Deferred, но я думаю, что он также должен поддерживать suspend fun. Вы объявили свою функцию как suspend fun?   -  person Marko Topolnik    schedule 15.10.2018
comment
Здесь поддержка suspend fun появится сразу после выхода Kotlin 1.3.   -  person Marko Topolnik    schedule 15.10.2018


Ответы (1)


Вам необходимо изменить свой CoroutineScope, чтобы не использовать повторно тот же Job. Он уже считается неудачным, поэтому даже не начнется выполнение.

См. связанную проблему на github.

person Pawel    schedule 14.10.2018
comment
Этот совет в корне неверен, он устраняет причину, по которой основная работа существует в первую очередь. Лучший совет: не позволяйте исключению ускользать без обработки. Если это не вариант, воспользуйтесь советом из проблемы GitHub и используйте SupervisorJob вместо Job. - person Marko Topolnik; 15.10.2018