1

Apparently I have failed to understand the logic of exception handling with kotlin coroutines. Could you please help me? On my Android app I have a button which fires the fetch function.

This function does 3 things:

  1. retrieves a json file from a server (getRequest) - might fail

  2. Processes the json file (processJSON) and stores 4 values temporarily inside the instance of the class Departures (departures). - might fail for another reason

  3. populates 4 TextView with those values. (uiUpdate) I would like all of those fields to be populated with one understandable error message generated by the catch clauses of these failed functions. How can I achieve this?

     fun fetch(stopID: String, attr: String, value: String) {
             lifecycleScope.launch(Dispatchers.IO) {
                 try {
                     val result = getRequest(stopID)
                     val departures = processJSON(result, attr, value)
                     withContext(Dispatchers.Main) {uiUpdate(stopID, departures)}
                 } catch (e: Exception) {
                 }
             }
         }
    

If I remove the try-catch here, the app crashes in case of an exception. If I put the uiUpdate into the shown catch clause it crashes again.

2 Answers 2

2

By default lifecycleScope uses Dispatchers.Main.immediate dispatcher. You can remove Dispatchers.IO from passing it to lifecycleScope.launch and just call suspend functions, wrapped into try-catch:

 fun fetch(stopID: String, attr: String, value: String) = lifecycleScope.launch {
         try {
             val result = getRequest(stopID)
             val departures = processJSON(result, attr, value)
             uiUpdate(stopID, departures)
         } catch (e: Exception) {
             // You can update UI here as well
         }
 }
Sign up to request clarification or add additional context in comments.

Comments

1

Thank you Sergey! The following function really does what I intended to achieve. I removed the try-catch from inside the functions and added the respective @Throws expressions before the declaration of these functions.

 fun fetch(stopID: String, attr: String, value: String) {
          lifecycleScope.launch(Dispatchers.Default) {
              try {
                 val result = getRequest(stopID)
                 val departures = processJSON(result, attr, value)
                 withContext(Dispatchers.Main) {uiUpdate(stopID, departures)}
             } catch (e: UnknownHostException) {
                  withContext(Dispatchers.Main) {uiUpdate(stopID, Departures("Error2","Error2"))}
             } catch (e: JSONException) {
                  withContext(Dispatchers.Main) {uiUpdate(stopID, Departures("Error3","Error3"))}
             } catch (e: Exception) {
                 withContext(Dispatchers.Main) {uiUpdate(stopID, Departures("Error4","Error4"))}
             }
        }
    }

1 Comment

You can remove all withContext(Main) if you remove the dispatcher from the launch call. It will be used by default. If you really need getRequest and/or processJSON to run on other dispatchers, you could wrap them in withContext with the dispatcher you want. Note that withContext returns the value of the last expression in the lambda, so you can get a result out of it: val departures = withContext(Dispatchers.Default) { processJSON(...) }

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.