Android handle all exceptions

Error handling on Android part 1: how exceptions work for JVM and Android apps

Error handling on Android

How many times have you been in the middle of using a new shiny app, only to have it crash on you?

This is the first in a series of posts that will investigate how the exception handling mechanism works in Java and Android, and how crash reporting SDKs can capture diagnostic information, so that you’re not flying blind in production.

How do exceptions work for JVM and Android apps?

Exceptions should be thrown in exceptional circumstances where the calling code needs to decide how to recover from an error condition.

What is an Exception object?

A [CODE]Throwable[/CODE] is a special type of object which can be thrown and alter the execution path of a JVM application. For example, the code snippet below throws an [CODE]IllegalStateException[/CODE]:

— CODE language-kotlin —
fun main() <
В В В try <
В В В В В В В throw IllegalStateException()
В В В В В В В println(«Hello World»)
В В В > catch (exc: Throwable) <
В В В В В В В println(«Something went wrong»)
В В В >
>

Throwing an exception means that the execution flow changes, and ‘Hello World’ is never printed. Instead, the program counter will jump to the nearest catch block, and executes the error recovery code within, meaning our program prints ‘Something went wrong’ instead.

Of course, it doesn’t always make sense to recover from a failure — for example, if an [CODE]OutOfMemoryError[/CODE] is thrown by the JVM, there is very little prospect of ever recovering from this condition. In this case it makes sense to leave the [CODE]Throwable as unhandled[/CODE], and allow the process to terminate so the user can restart the app from a fresh state.

Anatomy of the Throwable class

[CODE]Throwable[/CODE] has two direct subclasses: [CODE]Exception[/CODE], and [CODE]Error[/CODE]. Typically an [CODE]Error[/CODE] is thrown in conditions where recovery is not possible, and an [CODE]Exception[/CODE] where recovery is possible. Additionally, there are many subclasses of [CODE]Exception[/CODE] which convey additional meaning — for example, an [CODE]IllegalArgumentException[/CODE] would indicate the programmer passed an invalid argument, and an [CODE]IllegalStateException[/CODE] would indicate that the program encountered an unanticipated state.

— CODE language-kotlin —
fun main() <
В В В try <
В В В В В В В throw IllegalStateException(«This should never happen!»)
В В В В В В В println(«Hello World»)
В В В > catch (exc: Throwable) <
В В В В В В В println(«Something went wrong»)
В В В >
>

Let’s consider the above snippet again. The constructed [CODE]IllegalStateException[/CODE] object captures a snapshot of the application at the time of the error condition:

— CODE language-kotlin —
java.lang.IllegalStateException: This should never happen!
at com.example.myapplication.Exceptions101Kt.foo(Exceptions101.kt:12)
at com.example.myapplication.Exceptions101Kt.main(Exceptions101.kt:5)
at com.example.myapplication.Exceptions101Kt.main(Exceptions101.kt)

This is commonly called a stacktrace. Each line represents a single frame in the application’s call stack at the time of the error, which match the filename, method name, and line number of our original code snippet.

A stacktrace can also contain other useful information, such as program state, which in this case is a static error message, but we could equally pass in arbitrary variables.

Exception handling hierarchy

After throwing an exception, an exception handler must be found to handle the exception, or the app will terminate. In the JVM, this is a well-defined hierarchy, which we’ll run through here.

First up in the exception handling hierarchy is a catch block:

— CODE language-kotlin —
try <
В В В crashyCode()
> catch (exc: IllegalStateException) <
В В В // handle throwables of type IllegalStateException
>

If a catch block isn’t available in the current stack frame, but is defined further down the call stack, then the exception will be handled there.

Next in the hierarchy is implementations of [CODE]UncaughtExceptionHandler[/CODE]. This interface contains a single method which is invoked whenever a [CODE]Throwable[/CODE] is thrown, after the handler has been set:

— CODE language-kotlin —
val currentThread = Thread.currentThread()
currentThread.setUncaughtExceptionHandler < thread, exc ->
В В В // handle all uncaught JVM exceptions in the current Thread
>

It’s possible to set an [CODE]UncaughtExceptionHandler[/CODE] in a few different places; the JVM has a defined hierarchy for these. First, if a handler has been set on the current [CODE]Thread[/CODE], this will be invoked. Next up will be a handler on the [CODE]ThreadGroup[/CODE], before finally, the default handler is invoked, which will handle all uncaught JVM exceptions by printing a stacktrace, and then terminating the app.

— CODE language-kotlin —
Thread.setDefaultUncaughtExceptionHandler < thread, exc ->
В В В // handle all uncaught JVM exceptions
>

It’s the default [CODE]UncaughtExceptionHandler[/CODE] that is most interesting from an error reporting point-of-view, and it’s the default [CODE]UncaughtExceptionHandler[/CODE] that is responsible for showing that all too familiar crash dialog on Android.

Читайте также:  Когда точно будет android

The [CODE]UncaughtExceptionHandler[/CODE] interface is the building block of all crash reporting SDKs on the JVM, such as bugsnag-android or bugsnag-java. Read on in part two to learn how we can define a custom handler for uncaught exceptions, and use it to create a crash reporting SDK.

Would you like to know more?

Hopefully this has helped you learn a bit more about Error Handling on Android. If you have any questions or feedback, please feel free to get in touch.

Источник

Flexible way to handle exceptions in Android

May 4, 2018 · 4 min read

Introduction

As your project becomes bigger, you will inevitably face problems of error handling in different parts of your app. Error handling can be tricky — in some cases you should simply show a message to the user, while some errors require to perform navigation to a screen, etc…

The majority of developers use some kind of a struct u re pattern in the presentation layer of any project — MVP, MVVM, some guys use MVI… In most cases the exceptions which occurred in the data layer should be raised to the presentation layer for appropriate handling and showing message to the user. But it is not true for every case.

What do we expect from the ErrorHandler in our project?

  • We need to be able to handle various types of exceptions.
  • We want to use it not only in the presenters.
  • The code should be reusable.

Although, I will be talking in terms of MVP, but this approach will work for any kind of presentation pattern. And one more thing: it will work even at iOS

DIRTY IMPLEMENTATION

So, you are using MVP. I am pretty sure that most of you:

1. Use some kind of an MVP library for reducing boilerplate code(Moxy, Mosby, EasyMvp and etc.)

2. Have a BasePresenter that looks like this :

It also may contain code for attaching and detaching views and some code for surviving orientation changes. But in my case MvpPresenter will do it for me.

So I am with you, guys. =)

How does specific implementation of a BasePresenter actually look like?

Good. Let’s imagine, that you are not getting particular messages in error responses from backend and you have to handle it on the client side. Almost all presenters should be able to handle errors, so maybe you will have an idea to make something like this :

P.S. ResourceManager is just a wrapper around the application context, since we do not want to have Android dependencies in the presenters to ensure they are convenient for unit testing.

At this point, you can come up with a lot of details related to the implementation. For example, you can pass a lambda to the handleError function, or create a new abstract class ErrorHandlingPresenter which will extend BasePresenter and move the handleError function there. There is actually a lot of space for improvement. But this approach has some serious drawbacks:

  1. Your code is not SOLID. Your presenters are responsible for error handling.
  2. Your code is not reusable. Imagine that you need to process errors in the Retrofit Interceptor while you are trying to refresh your access token? What should you do? Just copy and paste the handleError function?

Let’s try to fix it.

THE RIGHT WAY

Let’s assume that the most common way to handle any error is to show a message to the user. Note: that is true almost for any project, but feel free to adjust it to your project requirements.

Читайте также:  Menu always visible android

Further, I will show you a complete hierarchy of classes with explanations.

First, we need an interface for the classes, which is able to show the error. In most cases it will be implemented by activity/fragments:

Interface for error handler :

Since our error handlers live in the presenters, and the presenters survive orientation changes, we cannot pass instance of the CanShowError view to the error handler constructor.

The following is the most common implementation of the ErrorHandler :

The implementation has a weak reference of the view, that is able to show errors to the user. It has a resource manager for fetching strings and it has the most simple and common logic of error processing. The DefaultErrorHandler will be a global singleton, and the views will be attached and detached from the ErrorHandler depending on what the screen had presented to the user.

Good. Now we need to inject ErrorHandler implementation to every presenter that is able to handle errors and not forget to attach and detach view to it. For this purpose I prefer using inheritance, so I have two base presenters: the most common one has CompositeSubscription and ErrorHandlingPresenter, that attaches and detaches view to error handler automatically.

Please pay attention to the generic constraints of the view types.

Now our specific presenters that are able to process errors will look like this:

With the help of the Dagger2 we can pass the DefaultErrorHandler as implementation of the ErrorHandler interface to constructor. But if you remember, the DefaultErrorHandler can only show errors, so in my case the business rules were: if you get a 404 Not found error from the backend, then perform navigation to another screen, if something else — show the error to the user.

So, I create a new implementation of the ErrorHandler :

Here I used composition and injecting the DefaultErrorHandler to the SpecificErrorHandler. Also, a router for performing screen navigation. In proceed function we try to catch 404 error and navigate to another screen, if we cannot — we delegate the control to the defaultErrorHandler. The same is with the ‘attach’ and ‘detach’ methods — the control is delegated to the defaultErrorHandler.

Since the DefaultErrorHandler is a global singleton, we should use Qualifier for injecting SpecificErrorHandler implementation.

That is it. Now it should work as expected.

As you can see, it does not depend on the MVP details, it is flexible, SOLID and we can use it in any class that can spawn errors.

Источник

Android: Error handling in Clean Architecture

How do you propagate errors between the layers in Clean Architecture?

We have started refactoring our android project for almost two years with Clean Architecture. Since that time the only topic that has been missing till now is error handling. Sometimes it’s confusing how to propagate errors crossing boundaries between layers.

Our Clean Architecture brief:

  • Domain : entities and business logic
  • Data : data sources, repositories, providers, 3rd-party services, platform-specific stuff…
  • Presentation : UI, view model, view logic

Where errors come from?

Most of the time errors come from the data layer, such as:

  • device errors: can not access database, permission denied accessing other app’s internal storage…
  • API failures: no internet connection, page not found, server error…
  • 3rd party provider errors: eg. too many requests at the same time for commute times service, access denied from firebase database…
  • and many more…

Sometimes we have exceptions occurring in domain but what we have here is all business logic that should be unit tested carefully and pretty stable afterward. If there is any error happens inside domain , it should be fixed in the development cycle (Runtime Exceptions) and shouldn’t be propagated upper layers.

It’s the same in presentation — the most top layer. When the view can’t be shown properly due to whatever error in the layout files or rendering codes, it shouldn’t be the case that we show an error message to users like “something went wrong, please try again later!”. Or if we can’t navigate from list to detail screen as the failure is your bundle data isn’t set correctly, the code should be fixed as well instead of showing the error to users.

Where should be the best place to handle errors?

Well, this is the biggest question when we think about error handling.

Sure that we will show the errors in the presentation layer and let our users know what happened and then allow them to do some actions such as reload data. But error handling is not only how we show the error, but also how we know what exactly is the error prior to showing it.

Читайте также:  Tunein radio pro android

The presentation layer — responsible for UI, shouldn’t know what is HttpException , what is 404 or 503 code… It should only know that this error is no internet connection, page not found or service unavailable…

The domain layer — responsible for business logic, doesn’t know what is API, database or any data providers.

Therefore, extracting error from an Exception in domain or presentation is absolutely not a good idea.

As described above, errors come from the data layer, hence, this is the only place knowing how all the errors happen.

Inversion of control and propagating errors from data to presentation

According to Clean Architecture, domain layer contains enterprise business rules and application business rules, so errors can be something that belongs to application business rules.

Error entity

We define an ErrorEntity with all of our error types like this:

Depends on the characteristics of your domain, you might have different error types.

Inversion of control

This error entity and the handler interface are placed in domain and should be implemented by data layer, that makes data depends on domain .

The implementation looks like this:

Propagating errors to presentation

Presentation layer calls use cases in domain without direct access to data layer, thus every error should be propagated through use cases.

This is a simple example use case without error handling:

And in presentation we call the use case like this:

Sometimes we need to combine multiple use cases in a single Rx chain like this:

As you see there would be so many questions regarding error propagating like the above examples.

So, to propagate ErrorEntity properly, we need a class called Result :

It’s clear enough for everyone to understand that for any use case call, we have 2 cases: either success or error.

With Kotlin extensions:

Then in presentation :

When the error comes to presentation layer, it’s clear about the type so the view can decide how to show it with corresponding actions easily. The important thing is that we know exactly that the error has been handled and there is no exception that can be thrown from use case for sure, so we safely don’t need to catch the Rx chain error anymore.

Another thing is that it’s harder to mix multiple use cases like the above example, any process will focus on its responsibility.

Another option

In some cases, you’d need to handle exceptions that are not likely to match the above ErrorHandler , such as reading files throwing IOException which is absolutely not is a network error. Another option could be: the Repository interface in domain layer can directly require already-handled-error for its return type, such as:

Conclusions

This actually made my life much easier whenever dealing with errors in an android project using Clean Architecture. Applying too many tricky rules is not so good but some simple rules are needed as it helps us to be fast-forward implementing features without repeatedly thinking about where should we handle this error, how the error looks like…

And the most important thing is that we should understand responsibility of each layer in the Clean Architecture and put things at the right place.

In some cases, you’d need to handle exceptions that are not likely to match the ErrorHandler above, such as reading files throwing IOException which is absolutely not is a network error. Another option could be: the Repository interface in domain layer can directly require already-handled-error for its return type, such as:

Direct error handling in repository interface

It really depends on characteristics of your domain, your application. The importance is the responsibility of each layer, and how to implement it in the best way is up to you.

Conclusions

This actually made my life much easier whenever dealing with errors in an android project using Clean Architecture. Applying too many tricky rules is not so good but some simple rules are needed as it helps us to be fast-forward implementing features without repeatedly thinking about where should we handle this error, what the error looks like, etc.

Источник

Оцените статью