Как использовать область сопрограмм для вызова веб-службы и обработки ошибки kotlin - PullRequest
2 голосов
/ 23 января 2020

Я разрабатываю приложение android с kotlin. Я использую сваггер, чтобы получить все свои веб-сервисы. Я хочу вызвать веб-сервис и обработать ошибку с помощью try / catch, если она существует. Спустя некоторое время исследования о том, как использовать этот WS, я обнаружил, что должен использовать сопрограмму с диспетчерами, я использую GlobalScope следующим образом:

         GlobalScope.launch(Dispatchers.Default) {
  val productsType = mobileApi.apiMobileProductTypeGetAllPost(params, 0, 50, "", "")
withContext(Dispatchers.Main) {
    viewProductType.loadAllTypeProduct(productsType)}

Вот код loadAllTypeProduct:

override fun loadAllTypeProduct(data: Array<ProductTypeData>) {
        recyclerViewProductTypeList.apply {
            recyclerViewProductTypeList.layoutManager = LinearLayoutManager(context, RecyclerView.HORIZONTAL, false)
            recyclerViewProductTypeList.adapter = ProductTypeItemAdapter(data, this@HomeFragment, context)
        }
    }

Но я обнаружил, что должен использовать Coroutine Scope вместо Global Scope, и Global Scope крайне обескуражен. Итак, я изменяю свой код из кода выше на следующий:

   val scope = CoroutineScope(Dispatchers.Main)
        basicViewInterface.showProgressBar()
        val task = scope.launch {
            try {
               withContext(Dispatchers.IO) {
                   val productsType=  mobileApi.apiMobileProductTypeGetAllPost(params, 0, 50, "", "")
                   viewProductType.loadAllTypeProduct(productsType)
                }
                basicViewInterface.hideProgressBar()
            } catch (e: Throwable) {
                e.printStackTrace()
            }
        }
        if (task.isCancelled){
            basicViewInterface.displayError("error")
        }

Я хочу вызвать мой WS и обработать исключение с помощью try / catch и отобразить ошибку в Toast. Как я могу это сделать ,

Ответы [ 2 ]

2 голосов
/ 23 января 2020

Смысл области сопрограммы заключается в том, что она доступна в том месте, где вы обнаруживаете, что пользователь ушел от текущей активности, так что вы можете отменить ее в одном центральном месте и отменить все дочерние сопрограммы. Ваше изменение, которое просто создает локальную одноразовую область, так же плохо, как и использование GlobalScope. Если ваш код находится внутри Activity или Fragment, то заставьте этот класс реализовать CoroutineScope by MainScope, вы можете увидеть более подробный пример в документации из CoroutineScope.

Что касается обработки за исключением того, что код должен выглядеть примерно так, как вы его разместили, просто обратите внимание на переключение контекстов. Область сопрограммы должна указывать Main в качестве диспетчера, и вам следует переключиться на IO только для блокирующего сетевого вызова, например:

basicViewInterface.showProgressBar()
scope.launch {
    try {
        val productsType = withContext(Dispatchers.IO) {
            mobileApi.apiMobileProductTypeGetAllPost(params, 0, 50, "", "")
        }
        viewProductType.loadAllTypeProduct(productsType)
        basicViewInterface.hideProgressBar()
    } catch (e: Throwable) {
        basicViewInterface.displayError("error")
    }
}

Было бы еще лучше и чище, если вы нажмете на это withContext в тело apiMobileProductTypeGetAllPost, потому что это беспокойство, локализованное для него. Со стороны это должна быть просто еще одна приостановляемая функция, которую можно вызывать, не беспокоясь о таких низкоуровневых деталях, как то, блокирует ли данная реализация неблокирование.

Я заметил, что другие упоминают обработчик исключений сопрограммы, но я не рекомендую использовать его. Он работает только на верхнем уровне иерархии сопрограмм и его целью является отлов только тех исключений, которые не были должным образом обработаны в бизнес-коде из-за ошибки программирования. Это эквивалент Thread.uncaughtExceptionHandler в Java.

1 голос
/ 23 января 2020

Если вы хотите обрабатывать исключения самостоятельно, вам необходимо использовать CoroutineExceptionHandler .

Документация гласит:

Необязательный элемент в контексте сопрограммы для обработки неперехваченных исключений.

Обычно неперехваченные исключения могут возникать только из сопрограмм, созданных с помощью компоновщика запуска. Сопрограмма, созданная с использованием async, всегда перехватывает все свои исключения и представляет их в результирующем отложенном объекте.

Как? Создайте объект этого обработчика исключений, а затем передайте его сопрограмме в следующем контексте:

// Create object of exception handler
val exceptionHandler = CoroutineExceptionHandler { coroutineContext, throwable ->
    // Here you can handle your exception
}

val scope = CoroutineScope(Dispatchers.Main)
basicViewInterface.showProgressBar()
// pass exception handler as context to launch method
val task = scope.launch(exceptionHandler) {
        withContext(Dispatchers.IO) {
            val productsType=  mobileApi.apiMobileProductTypeGetAllPost(params, 0, 50, "", "")
            viewProductType.loadAllTypeProduct(productsType)
        }
        basicViewInterface.hideProgressBar()
    }
...