Error handling library for Android and Java

Overview

ErrorHandler

Download Travis

Error handling library for Android and Java

Encapsulate error handling logic into objects that adhere to configurable defaults. Then pass them around as parameters or inject them via DI.

Download

Download the latest JAR or grab via Maven:

<dependency>
  <groupId>com.workable</groupId>
  <artifactId>error-handler</artifactId>
  <version>1.1.0</version>
  <type>pom</type>
</dependency>

or Gradle:

compile 'com.workable:error-handler:1.1.0'

Usage

Let's say we're building a messaging Android app that uses both the network and a local database.

We need to:

Setup a default ErrorHandler once

  • Configure the default ErrorHandler
  • Alias errors to codes that are easier to use like Integer, String and Enum values
  • Map errors to actions to take when those errors occur (exceptions thrown)
// somewhere inside MessagingApp.java

ErrorHandler
  .defaultErrorHandler()

  // Bind certain exceptions to "offline"
  .bind("offline", errorCode -> throwable -> {
      return throwable instanceof UnknownHostException || throwable instanceof ConnectException;
  })

  // Bind HTTP 404 status to 404
  .bind(404, errorCode -> throwable -> {
      return throwable instanceof HttpException && ((HttpException) throwable).code() == 404;
  })

  // Bind HTTP 500 status to 500
  .bind(500, errorCode -> throwable -> {
      return throwable instanceof HttpException && ((HttpException) throwable).code() == 500;
  })

  // Bind all DB errors to a custom enumeration
  .bindClass(DBError.class, errorCode -> throwable -> {
      return DBError.from(throwable) == errorCode;
  })

  // Handle HTTP 500 errors
  .on(500, (throwable, errorHandler) -> {
    displayAlert("Kaboom!");
  })

  // Handle HTTP 404 errors
  .on(404, (throwable, errorHandler) -> {
    displayAlert("Not found!");
  })

  // Handle "offline" errors
  .on("offline", (throwable, errorHandler) -> {
    displayAlert("Network dead!");
  })

  // Handle unknown errors
  .otherwise((throwable, errorHandler) -> {
    displayAlert("Oooops?!");
  })

  // Always log to a crash/error reporting service
  .always((throwable, errorHandler) -> {
    Logger.log(throwable);
  });

Use ErrorHandler inside catch blocks

// ErrorHandler instances created using ErrorHandler#create(), delegate to the default ErrorHandler
// So it's actually a "handle the error using only defaults"
// i.e. somewhere inside MessageListActivity.java
try {
  fetchNewMessages();
} catch (Exception ex) {
  ErrorHandler.create().handle(ex);
}

Run blocks of code using ErrorHandler.run

ErrorHandler.run(() -> fetchNewMessages());

Override defaults when needed

// Configure a new ErrorHandler instance that delegates to the default one, for a specific method call
// i.e. somewhere inside MessageListActivity.java
try {
  fetchNewMessages();
} catch (Exception ex) {
  ErrorHandler
    .create()
    .on(StaleDataException.class, (throwable, errorHandler) -> {
        reloadList();
        errorHandler.skipDefaults();
    })
    .on(404, (throwable, errorHandler) -> {
        // We handle 404 specifically on this screen by overriding the default action
        displayAlert("Could not load new messages");
        errorHandler.skipDefaults();
    })
    .on(DBError.READ_ONLY, (throwable, errorHandler) -> {
        // We could not open our database to write the new messages
        ScheduledJob.saveMessages(someMessages).execute();
        // We also don't want to log this error because ...
        errorHandler.skipAlways();
    })
    .handle(ex);
}

Things to know

ErrorHandler is thread-safe.

API

Initialize

  • defaultErrorHandler() Get the default ErrorHandler.

  • create() Create a new ErrorHandler that is linked to the default one.

  • createIsolated() Create a new empty ErrorHandler that is not linked to the default one.

Configure

  • on(Matcher, Action) Register an Action to be executed if Matcher matches the error.

  • on(Class<? extends Exception>, Action) Register an Action to be executed if error is an instance of Exception.

  • on(T, Action) Register an Action to be executed if error is bound to T, through bind() or bindClass().

  • otherwise(Action) Register an Action to be executed only if no other Action gets executed.

  • always(Action) Register an Action to be executed always and after all other actions. Works like a finally clause.

  • skipFollowing() Skip the execution of any subsequent Actions except those registered via always().

  • skipAlways() Skip all Actions registered via always().

  • skipDefaults() Skip any default actions. Meaning any actions registered on the defaultErrorHandler instance.

  • bind(T, MatcherFactory<T>) Bind instances of T to match errors through a matcher provided by MatcherFactory.

  • bindClass(Class<T>, MatcherFactory<T>) Bind class T to match errors through a matcher provided by MatcherFactory.

  • clear() Clear all registered Actions.

Execute

  • handle(Throwable) Handle the given error.

About

When designing for errors, we usually need to:

  1. have a default handler for every expected error // i.e. network, subscription errors
  2. handle specific errors as appropriate based on where and when they occur // i.e. network error while uploading a file, invalid login
  3. have a catch-all handler for unknown errors // i.e. system libraries runtime errors we don't anticipate
  4. keep our code DRY

Java, as a language, provides you with a way to do the above. By mapping cross-cutting errors to runtime exceptions and catching them lower in the call stack, while having specific expected errors mapped to checked exceptions and handle them near where the error occurred. Still, countless are the projects where this simple strategy has gone astray with lots of errors being either swallowed or left for the catch-all Thread.UncaughtExceptionHandler. Moreover, it usually comes with significant boilerplate code. ErrorHandler however eases this practice through its fluent API, error aliases and defaults mechanism.

This library doesn't try to solve Java specific problems, although it does help with the log and shallow anti-pattern as it provides an opinionated and straightforward way to act inside every catch block. It was created for the needs of an Android app and proved itself useful very quickly. So it may work for you as well. If you like the concept and you're developing in Swift or Javascript, we're baking 'em and will be available really soon.

License

The MIT License

Copyright (c) 2013-2016 Workable SA

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Comments
  • Need a way to define a common action based on the type of error code

    Need a way to define a common action based on the type of error code

    Say I have a bunch of bind calls that map exceptions to value of MyEnum class as in:

    .bind(MyEnum.Foo, errorCode -> throwable -> throwable instanceof SomeException)
    .bind(MyEnum.Bar, errorCode -> throwable -> throwable instanceof SomeOtherException)
    

    I would like to define an action to take for any value of MyEnum type as in:

    .on(MyEnum.class, (enumValue, throwable, errorHandler) -> doSomething())
    
    question 
    opened by dalewking 9
  • How can we use in a complex project

    How can we use in a complex project

    Hi please advise how we can implement in project where erros can happen at multiple places like service call (Retrofit lib used in background thread, UI vaidation error in fragment...). Something like this project https://github.com/googlesamples/android-architecture/tree/dev-todo-mvvm-live

    question 
    opened by birender-s 6
  • Would be nice to have a shorthand bind call for mapping directly from exception type to error code

    Would be nice to have a shorthand bind call for mapping directly from exception type to error code

    A signature like this:

    public <T> ErrorHandler bind(T errorCode, Class<Throwable> types...)
    

    That basically says any if the exception is any of the given types map it to the given error code.

    question 
    opened by dalewking 4
  • Add possibility to specify parentErrorHandler

    Add possibility to specify parentErrorHandler

    Hello, everyone, thanks you for this useful framework. I faced the problem when I need some parentErrorHandler, but I can't use singleton object. I think my issue can be simply solved just by adding factory method. public static ErrorHandler create(ErrorHandler parentErrorHandler) { return new ErrorHandler(parentErrorHandler); }

    What do you think about it?

    enhancement 
    opened by androidmitry 3
  • Isn't the example going to throw ClassCastException?

    Isn't the example going to throw ClassCastException?

    The readme example has this declaration:

      // Bind HTTP 404 status to 404
      .bind(404, errorCode -> throwable -> {
          return ((HttpException) throwable).code() == 404;
      })
    
      // Bind HTTP 500 status to 500
      .bind(500, errorCode -> throwable -> {
          return ((HttpException) throwable).code() == 500;
      })
    

    If the error given to handle method is not an HttpException won't you get a ClassCastException?

    question 
    opened by dalewking 3
  • ErrorHandler does not respect predefined actions

    ErrorHandler does not respect predefined actions

    Let's say that we have an default ErrorHandler with this setup:

    ErrorHandler
        .defaultErrorHandler()
        .bind("offline", errorCode -> throwable -> {
            return throwable instanceof ConnectException ||
                       throwable instanceof UnknownHostException
        })
        .on("offline", (throwable, handler) -> { /* do global action */ });
    

    and somewhere in my code I call:

    ErrorHandler
        .create()
        .skipDefaults()
        .on("offline", (throwable, handler) -> { /* do something */ })
        .handle(e);
    

    The problem is that the ErrorHandler executes the predefined action that I defined on the default one.

    bug 
    opened by charbgr 1
Owner
null
🧹 Error correcting parser plugin

Tidyparse The main goal of this project is to speed up the process of learning a new language by suggesting ways to fix source code. Tidyparse expects

breandan 5 Dec 15, 2022
Gesture detector framework for multitouch handling on Android, based on Android's ScaleGestureDetector

Android Gesture Detectors Framework Introduction Since I was amazed Android has a ScaleGestureDetector since API level 8 but (still) no such thing as

null 1.1k Nov 30, 2022
General purpose utilities and hash functions for Android and Java (aka java-common)

Essentials Essentials are a collection of general-purpose classes we found useful in many occasions. Beats standard Java API performance, e.g. LongHas

Markus Junginger 1.4k Dec 29, 2022
General purpose utilities and hash functions for Android and Java (aka java-common)

Essentials Essentials are a collection of general-purpose classes we found useful in many occasions. Beats standard Java API performance, e.g. LongHas

Markus Junginger 1.4k Dec 29, 2022
WebSocket & WAMP in Java for Android and Java 8

Autobahn|Java Client library providing WAMP on Java 8 (Netty) and Android, plus (secure) WebSocket for Android. Autobahn|Java is a subproject of the A

Crossbar.io 1.5k Dec 9, 2022
WebSocket & WAMP in Java for Android and Java 8

Autobahn|Java Client library providing WAMP on Java 8 (Netty) and Android, plus (secure) WebSocket for Android. Autobahn|Java is a subproject of the A

Crossbar.io 1.5k Dec 9, 2022
Trail is a simple logging system for Java and Android. Create logs using the same API and the library will detect automatically in which platform the code is running.

Trail Trail is a simple logging system for Java and Android. Create logs using the same API and the library will detect automatically in which platfor

Mauricio Togneri 13 Aug 29, 2022
UPnP/DLNA library for Java and Android

Cling EOL: This project is no longer actively maintained, code may be outdated. If you are interested in maintaining and developing this project, comm

4th Line 1.6k Jan 4, 2023
Multiplaform kotlin library for calculating text differences. Based on java-diff-utils, supports JVM, JS and native targets.

kotlin-multiplatform-diff This is a port of java-diff-utils to kotlin with multiplatform support. All credit for the implementation goes to original a

Peter Trifanov 51 Jan 3, 2023
A low intrusive, configurable android library that converts layout XML files into Java code to improve performance

qxml English 一个低侵入,可配置的 Android 库,用于将 layout xml 文件转换为 Java 代码以提高性能。 与X2C的对比 X2C: 使用注解处理器生成View类,使用时需要在类中添加注解,并替换setContentView方法,侵入性较强; 对于布局属性的支持不够完美

null 74 Oct 6, 2022
java.io.File compatible SAF library

DocumentFileX java.io.File compatible SAF implementation Tired of SAF bullshits? Implement SAF with ease! This library is in alpha stage. Most feature

null 24 Aug 25, 2022
MMDUtils is a library for read/write mmd related file in java

MMDUtils MMDUtils is a library for read/write mmd related file in java Features Read/Write VMD(Vocaloid Motion Data) file Read/Write PMX(Polygon Model

null 5 Jan 28, 2022
gRPC and protocol buffers for Android, Kotlin, and Java.

Wire “A man got to have a code!” - Omar Little See the project website for documentation and APIs. As our teams and programs grow, the variety and vol

Square 3.9k Dec 31, 2022
a simple cache for android and java

ASimpleCache ASimpleCache 是一个为android制定的 轻量级的 开源缓存框架。轻量到只有一个java文件(由十几个类精简而来)。 1、它可以缓存什么东西? 普通的字符串、JsonObject、JsonArray、Bitmap、Drawable、序列化的java对象,和 b

Michael Yang 3.7k Dec 14, 2022
A lightning fast, transactional, file-based FIFO for Android and Java.

Tape by Square, Inc. Tape is a collection of queue-related classes for Android and Java. QueueFile is a lightning-fast, transactional, file-based FIFO

Square 2.4k Dec 30, 2022
BinGait is a tool to disassemble and view java class files, developed by BinClub.

BinGait Tool to diassemble java class files created by x4e. Usage To run BinGait, run java -jar target/bingait-shadow.jar and BinGait will launch. If

null 18 Jul 7, 2022
Java implementation of a Disk-based LRU cache which specifically targets Android compatibility.

Disk LRU Cache A cache that uses a bounded amount of space on a filesystem. Each cache entry has a string key and a fixed number of values. Each key m

Jake Wharton 5.7k Dec 31, 2022
Runtime code generation for the Java virtual machine.

Byte Buddy runtime code generation for the Java virtual machine Byte Buddy is a code generation and manipulation library for creating and modifying Ja

Rafael Winterhalter 5.3k Jan 7, 2023
Apk parser for java

APK parser lib, for decoding binary XML files, getting APK meta info. Table of Contents Features Get APK-parser Usage 1. APK Info 2. Get Binary XML an

Hsiafan 1.1k Jan 2, 2023