Getting started with Raygun Crash Reporting using Raygun4Android
Posted Feb 26, 2021 | 10 min. (1949 words)Building native mobile apps can be a daunting challenge. Even if you ignore the choice of building separate iOS/Android apps or using cross-platform technology, writing native apps is hard.
The development process of native apps involves a lot of testing and debugging. While the app is still under initial development this usually happens on test devices of the developers or dedicated testers. When problems arise or the app crashes, these incidents are often observable and can be debugged.
Native apps are usually distributed via app stores like Google Play or Apple’s App Store. After the user has installed an app on their phone, it moves outside of the direct reach of developers and testers. It becomes almost impossible to replicate and observe a crash on a user’s device. Retrieving log files from a customer’s Android device is not an achievable goal.
It’s never a good impression if users and customers experience bugs or if an app crashes and disappears from the screen. It will drive customers away and threaten your organization’s reputation.
Raygun’s Android crash reporting allows you to track and learn more about the bugs and resulting crashes your users experience in your app. This article will explain how Raygun4Android, Raygun’s data provider for Android, is being integrated into a real, small sample app and how it can help you to unveil the bugs and crashes your users experience in your apps.
Quick navigation
Setup
To follow along, start by having a look at the sample app for this article. The repository contains an Android Studio project that imports into Android Studio 4.1+. Checkout the tag start
to look at the version of the code where we’re starting off this tutorial.
A list of movies
When you build and launch the sample app on your device or an emulator, you will land on a screen showing a list of movies from Studio Ghibli. It uses the Ghibli API’s films
endpoint to retrieve an array of movies. It then uses that to display some basic information in an Android RecyclerView
component.
This screen is implemented in the MainActivity
class. The method fetchAndDisplayFilmsList()
handles fetching and rendering the data from the API.
Detail screen
Tapping on one of the list elements leads you to a detail screen. This screen shows some extra information about the selected movie and loads a random robot image from Robohash.org. You can go back to the list screen by tapping the back button in the app’s top navigation bar. If you’re using gesture navigation, swipe from the left.
You’ll find the implementation of this screen in DetailActivity
. It is using its own ViewModel
and Android data binding to display the information on the screen.
A form to add another movie
Back on the main screen, you can also tap on the FloatingActionButton
labeled with the plus sign. This will lead you to a third screen displaying a form to add a new movie. The movie will only be stored locally on your device and no data will be sent anywhere. The code driving the form is in FormActivity
.
Adding Raygun4Android to the mix
Let’s assume our sample app is going into production and is going to be installed by thousands of users all over the world. We are happy with it and think it’s free of any bugs or other issues and launch in Google Play.
But when the first reviews come in, a noticeable amount of users are complaining about random crashes. Some people say the app is stuck before even showing the list screen. You immediately assume that there’s some kind of issue with the data loaded from API. It could also be an issue with binding the API’s data to the view.
There’s no way to tell for sure because the app in its current form has no support for any type of crash reporting and logging that would be accessible to you.
This is where Raygun Crash Reporting and Raygun4Android will help you. Start by creating an account for a free 14-day trial or log into your existing Raygun account.
Next, go through the process of creating a new application in the Raygun system. You will be given an API key for this new app.
Setting up the library
The first step in the setup process is to make sure that the Raygun4Android library is loaded into your app. To do that you will usually only have to add an implementation
dependency to your app’s build.gradle
file. In the same file, also make sure that you’re compiling with Java 8.
android {
...
compileOptions {
sourceCompatibility 1.8
targetCompatibility 1.8
}
...
}
...
dependencies {
...
implementation 'com.raygun:raygun4android:4.+'
...
}
Raygun4Android lives on Maven Central. Depending on your existing project configuration you will have to add mavenCentral()
to the repository list in your project’s build.gradle
file:
allprojects {
repositories {
...
mavenCentral()
...
}
}
Your API key goes into your app’s AndroidManifest.xml
file. Inside the <application>
element add:
<meta-data android:name="com.raygun.raygun4android.apikey"
android:value="YOUR_API_KEY_HERE" />
and insert your API key as the value
.
While you’re editing the manifest file, make sure to add the Raygun services to the manifest. The provider supports both Crash Reporting as well as RUM (Raygun’s real-time user monitoring). For the purposes of this article you only need to add the CrashReportingPostService
:
<service android:name="com.raygun.raygun4android.services.CrashReportingPostService"
android:exported="false"
android:permission="android.permission.BIND_JOB_SERVICE"
android:process=":crashreportingpostservice" />
The final step is to double-check that your app has internet and network permissions. These days most apps would have both already. To avoid any issues further down the line it’s always a good idea to make sure everything is in place.
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
Android crash reporting 101
At this point, you’re set up and all left to do is a small code change to enable the provider and its crash reporting features. Place the following two lines in your start-up activity to launch Raygun when the application starts. In our sample app, this would be MainActivity.
RaygunClient.init(applicationContext as Application)
RaygunClient.enableCrashReporting()
Some Android apps have a dedicated Application class. That is being used to set up application-wide infrastructure. If your app’s architecture provides such a class, it would be a good alternative location to initialize Raygun4Android for crash reporting. In this case, you would call the .init()
function with a reference to this
: RaygunClient.init(this)
.
From here on, Raygun4Android will pickup uncaught exceptions in your app and report them to the Raygun system. They will be tagged as UncaughtException
. Internally that works by replacing Android’s default exception handler class with an instance of RaygunUncaughtExceptionHandler
. The exception is then caught by Raygun4Android’s handler first and reported. Only then it’s passed on to the original default handler of the system.
static void attachExceptionHandler() {
Thread.UncaughtExceptionHandler oldHandler = Thread.getDefaultUncaughtExceptionHandler();
if (!(oldHandler instanceof RaygunUncaughtExceptionHandler)) {
CrashReporting.handler = new RaygunUncaughtExceptionHandler(oldHandler);
Thread.setDefaultUncaughtExceptionHandler(CrashReporting.handler);
}
}
The injection process into the exception handling flow is the reason why you are strongly encouraged to ever use a single exception handling and crash reporting library at any given time. If there were many crash reporting tools hooking into the exception handling system of your app, you would end up with very unpredictable results.
Reporting errors
The easiest way to trigger crash reporting is to force an error or exception to happen. MainActivity
’s fetchAndDisplayFilmsList()
method handles loading and binding the movie list data to the view.
If you add some code to the beginning of the method to trigger an exception, we will see it being logged to Raygun:
private fun fetchAndDisplayFilmsList() {
var i = 3/0
doAsync(exceptionHandler = { throwable: Throwable -> throwable.printStackTrace() }) {
...
}
}
The example shows a division by zero being added. Launch the sample app with this change and it will crash at startup. As a result, we will see the exception’s details being logged in the Raygun Crash Reporting system.
Custom reporting
On top of the automatic reporting on unhandled exceptions, you’re able to send extra reports to Raygun. To do this, you can call RaygunClient.send(Throwable throwable)
by yourself. A common scenario involves places where you have try/catch-structures in place. An exception here would not be unhandled anymore and therefore you will have to deal with any crash reporting yourself.
There are two additional variations of the .send()
-method that allow you to pass tags or custom data into the Raygun system. You can use them to provide more information to your crash report:
RaygunClient.send(Throwable throwable, List tags)
RaygunClient.send(Throwable throwable, List tags, Map customData)
Offline? That’s ok!
We’ve seen that Raygun4Android sends your crash reports either automatically or via calling .send()
. You might wonder what happens if a crash occurs while a device is offline. The short answer is: it’s stored on the device temporarily as a .raygun4
file and will be sent later when the device comes back online.
Why is this useful? Even without the user switching a device into airplane mode, mobile devices change connectivity regularly - quite often without the user noticing. There might be a handover from your local WiFi at home to a 4G/LTE network when leaving your house. Or you’re driving on a highway and roaming between mobile network towers at a high speed. Raygun4Android tries to cover connectivity losses as much as possible so that ideally no crash in your app will go unreported.
Don’t forget the context of your apps
It is important to consider the context of your app and its architecture. Sometimes other 3rd-party frameworks and libraries have unexpected behavioral patterns. Here’s an example in our movie app.
The Ghibli movie app uses AnKo, a Kotlin framework to manage asynchronous calls. As you’ve seen further up, an async thread is started in fetchAndDisplayFilmsList()
with a doAsync
statement.
doAsync
has an interesting default behavior: It swallows any exception and deals with them inside the framework in its own way. While that can be useful, it can be in the way of integrating a crash reporting solution like Raygun4Android.
If you were to use the most basic syntax for doAsync
you would never see an exception from your asynchronous code being reported in Raygun:
doAsync {
...
}
Luckily, doAsync
allows you to pass an exceptionHandler
lambda. The one we’ve been using in the sample app however doesn’t help us much. It takes the Throwable
and then just prints a stack trace to the console:
doAsync(exceptionHandler = { throwable: Throwable -> throwable.printStackTrace() }) {
...
}
Instead, we need to tell AnKo to use RaygunClient
in case an exception needs to be handled inside the asynchronous process:
doAsync(exceptionHandler = { throwable: Throwable -> RaygunClient.send(throwable) }) {
...
}
Or you could write your own exception handler for AnKo and make sure that you call RaygunClient.send()
from it.
What’s next
Give Raygun4Android a try and have a play with the sample app. There are a couple of places in the sample app where it’d be useful to apply some custom calls to RaygunClient.send()
. Think about API calls going wrong or issues with user data entered in the form to add a new movie.
In future articles of this series, we’re going to add more advanced Raygun Crash Reporting features to our sample Ghibli movie app. You’ll learn more about the build infrastructure around Proguard and Raygun4Android’s Gradle tasks.
And finally, we’re going to implement the real-time user monitoring component of Raygun4Android. Stay tuned and check out the Raygun4Android documentation on Github in the meantime.
Did you spend too much time discovering your bug in the first place? Raygun helps you detect, diagnose, and destroy errors in your Android code. Smart alerts and error grouping means you never miss a bug in your app again. Read more about Android crash reporting.