A lot of professionally made apps use a bug tracking service, which collects errors and crashes of all users and lets us, as the developers, further improve the app by fixing the issues. The default Retrofit request callbacks offer an onFailure()
method, which throws a variety of possible errors in one bucket.
In this tutorial, you'll learn how you can separate those errors and what our recommendation in dealing with them is.
Retrofit Series Overview
- Getting Started and Creating an Android Client
- Basics of API Description
- Creating a Sustainable Android Client
- URL Handling, Resolution and Parsing
- How to Change API Base Url at Runtime
- Multiple Server Environments (Develop, Staging, Production)
- Share OkHttp Client and Converters between Retrofit Instances
- Upgrade Guide from 1.9
- Beyond Android: Retrofit for Java Projects
- How to use OkHttp 3 with Retrofit 1
- Synchronous and Asynchronous Requests
- Send Objects in Request Body
- Add Custom Request Header
- Manage Request Headers in OkHttp Interceptor
- Dynamic Request Headers with @HeaderMap
- Multiple Query Parameters of Same Name
- Optional Query Parameters
- Send Data Form-Urlencoded
- Send Data Form-Urlencoded Using FieldMap
- How to Add Query Parameters to Every Request
- Add Multiple Query Parameter With QueryMap
- How to Use Dynamic Urls for Requests
- Constant, Default and Logic Values for POST and PUT Requests
- Cancel Requests
- Reuse and Analyze Requests
- Optional Path Parameters
- How to Send Plain Text Request Body
- Customize Network Timeouts
- How to Trust Unsafe SSL certificates (Self-signed, Expired)
- Dynamic Endpoint-Dependent Interceptor Actions
- How to Update Objects on the Server (PUT vs. PATCH)
- How to Delete Objects on the Server
- Introduction to (Multiple) Converters
- Adding & Customizing the Gson Converter
- Implementing Custom Converters
- How to Integrate XML Converter
- Access Mapped Objects and Raw Response Payload
- Supporting JSON and XML Responses Concurrently
- Handling of Empty Server Responses with Custom Converter
- Send JSON Requests and Receive XML Responses (or vice versa)
- Unwrapping Envelope Responses with Custom Converter
- Wrapping Requests in Envelope with Custom Converter
- Define a Custom Response Converter
- Simple Error Handling
- Error Handling for Synchronous Requests
- Catch Server Errors Globally with Response Interceptor
- How to Detect Network and Conversion Errors in onFailure
Tracking Retrofit's onFailure()
In the majority of our tutorials, we oversimplify the onFailure()
callback and assume it's only called on network failures. As an example, let's look at one of our Retrofit call snippets:
call.enqueue(new Callback<List<GitHubRepo>>() {
@Override
public void onResponse(Call<List<GitHubRepo>> call, Response<List<GitHubRepo>> response) {
if (response.isSuccessful()) {
Toast.makeText(ErrorHandlingActivity.this, "server returned data", Toast.LENGTH_SHORT).show();
// todo display the data instead of just a toast
}
else {
Toast.makeText(ErrorHandlingActivity.this, "Server returned an error", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFailure(Call<List<GitHubRepo>> call, Throwable t) {
Toast.makeText(ErrorHandlingActivity.this, "network failure :( inform the user and possibly retry", Toast.LENGTH_SHORT).show();
}
});
In our experience developers handle the onFailure()
the same way: they just show a toast indicating an Internet connection issue, like the code above does, or use a similar UI hint. Additionally, many developers don't actually log the failure with a bug tracking service, like hockeyapp or instabug.
This is understandable, because 98% of the thrown errors in onFailure()
will actually be Internet connection issues. When the user doesn't have an Internet connection and the request fails because of that, there is nothing we as the developer can do. It's not really necessary to track them.
Converter Errors
Unfortunately, one nasty cause for bugs can be missed this way: converter errors. When the converter (Gson, SimpleXML, …) fails due to some mismatch of the response payload and Java models, it'll also be thrown to the onFailure()
callback.
When developing apps with Retrofit, a significant part of the time is fine-tuning the Java models to perfectly fit the API responses. Of course, during development everything works well. But once the app is out in production across thousands of users, the chances of some very rare mapping error occurring are increased significantly. Only if you're tracking the thrown onFailure
callbacks, you'll be notified of this and are able to fix the bug.
Additionally, you also have a nice additional and cost-free layer of making sure changes to the API are not breaking the apps of existing users.
Distinguishing Error Causes
At this point, we've hopefully persuaded you to log onFailure()
callbacks, at least the ones that were caused by a converter, to the central bug tracking service of your choice. But how can you distinguish between network errors and converter errors?
The onFailure()
callback gives you as the second parameter the actual error object. You can simply check if the cause of the error was part of the Internet connection issue family (all part of IOException
), or not:
@Override
public void onFailure(Call<List<BrokenGitHubRepo>> call, Throwable t) {
if (t instanceof IOException) {
Toast.makeText(ErrorHandlingActivity.this, "this is an actual network failure :( inform the user and possibly retry", Toast.LENGTH_SHORT).show();
// logging probably not necessary
}
else {
Toast.makeText(ErrorHandlingActivity.this, "conversion issue! big problems :(", Toast.LENGTH_SHORT).show();
// todo log to some central bug tracking service
}
}
In the callback above we've provoked a conversion error with changing from GitHubRepo
to the BrokenGitHubRepo
model. When the user has an Internet connection, Retrofit will throw the converter exception to the onFailure()
callback. Because it's not an IOException
, you can safely log it to your bug tracking service.
This way you're aware of rare, but still possible conversion issues, and are not getting thousands of connection issue reports. We prefer this approach of error logging in the onFailure()
method.
Summary
In this tutorial you've learned why tracking onFailure()
errors can be very beneficial. In particular, we've shown you how you can distinguish between regular Internet connection issues and fatal conversion errors.
Do you have further questions on this topic or about Retrofit in general? Just let us know on Twitter @futurestud_io or leave a comment below.
Enjoy coding & make it rock!