How to Improve Your Android Debugging Process
Debugging Android apps has its unique challenges with crashes, ANRs, and inconsistent logs. But there are some easy ways to quickly identify and resolve issues, ensuring better performance and user experience. In this blog, we'll explore basic tools and techniques to streamline Android debugging, talk about ANRs, and then go deeper with how Sentry can track errors and provide insights to help you prevent future issues.
Three quick hit Android debugging basics
1. Logcat in Android Studio
Logcat provides real-time logs, helping you find errors manually. Within the tool, you can use custom tags to isolate specific issues:
Log.d("MyAppTag", "App crashed due to missing variable");
When you filter by "MyAppTag" in Logcat, you can focus on specific problems, such as a NullPointerException, without sifting through unrelated logs.
2. Breakpoints in Android Studio
Breakpoints are indispensable for inspecting runtime behavior. Set a breakpoint at a key line of code:
int result = calculateValue(); // Set a breakpoint here
When the breakpoint triggers, Android Studio will pause execution, allowing you to inspect variables (result) and step through code execution to see how the logic unfolds. This method is crucial for catching subtle bugs in flow control or calculations.
3. Sentry for Real-time Error Tracking
Instead of relying solely on local development debugging or manual logs, Sentry automates error tracking in production. For example, if an API request fails:
Sentry.captureException(Exception("API response error"));
Sentry captures the error and generates detailed reports with stack traces, breadcrumbs (events leading up to the error), and environmental details. This context is invaluable for diagnosing and resolving issues after your app is live. Sentry also can integrate with Logcat by capturing and enhancing error reports from Logcat logs with additional context like stack traces and breadcrumbs.
Now that we’ve covered the basics, let’s dive deeper into other strategies for debugging Android, including how to tackle ANRs, a very frequent and common issue in Android applications.
Detecting ANRs in Android
ANRs (Application Not Responding) occur when the main thread of an app is blocked for too long, causing the system to display an error dialog. The most common type is the Input Dispatching Timeout, which happens when the app’s main thread is unresponsive for more than 5 seconds following user input. This impacts user experience and can lead to app downgrades in the Google Play Store.
There are several methods for detecting ANRs in Android, each with its own benefits and trade-offs:
1. Watchdog Threads: This involves sending periodic tasks to the main thread. If the thread fails to respond within a set time (typically 5 seconds), it’s assumed that an ANR has occurred. This approach offers real-time detection but may generate false positives if there's no user interaction.
2. Native Signal Handlers: By installing a signal handler at the native level, it’s possible to listen for ANRs raised by the system. This approach can capture native signals, but it doesn’t cover background ANRs and provides limited thread information.
3. Application Exit Info API (Android 11+): This newer Android API provides the most accurate detection of ANRs, including full stack traces, thread information, and deadlock detection. It distinguishes between foreground and background ANRs but is only available on Android 11+.
Detecting ANRs is only one-half of the equation. Let’s talk about debugging Android ANRs, specifically using Sentry.
Debugging ANRs with Sentry
When an ANR occurs, Sentry automatically captures key details, including the state of the app at the time, device performance (e.g., memory and battery levels), and a full thread dump, which helps you identify blocking operations.
For instance, in a real-world scenario, let’s say your app experiences an Input Dispatching Timeout due to a thread-locking issue. With Sentry, you’d receive a detailed report showing the exact thread that is causing the blockage, including the specific line of code where it occurs. You can then pinpoint whether it’s a synchronization issue or something else locking the main thread.
Using Performance Profiling to Solve ANRs
Sentry’s profiling tool adds another layer of debugging capability by taking periodic snapshots of your app’s execution. These snapshots are visualized in a flame graph, which highlights functions that are taking the most time to execute. If a function in the UI thread is running too long, this could be the root cause of your ANR. Using this data, you can refactor your code to move intensive tasks off the main thread.
By integrating Sentry’s real-time error tracking and advanced profiling, you can more easily detect, debug, and prevent ANRs, ensuring a smoother experience for your users. We go deeper into this topic in this recorded workshop here.
Setting up Sentry for Android and getting the most out of it
Getting Senty for Android up and running is incredibly easy, whether you’re coding in Java or a language like Kotlin. Plus the dependency only adds around 200kb to your APK, which is pretty negligible even if you’ve shrunk the size of your app as much as possible, either using ProGuard{:rel=”nofollow”}{:target=”_blank”} or a converted Wayne Szalinski shrink ray that works on apps instead of his family.
This standard implementation records errors when they occur, shows you a stack trace of where it occurred, and provides state about the user’s phone at the time, such as battery level, which version of Android they’re using, what hardware they have, the orientation of the phone, how much disk space is available, whether the device is rooted or not, etc. You could stop right there and feel pretty good about your error monitoring.
However, there are some additional things Sentry can do to enhance your error reports and help your team understand problems that might otherwise be confusing. Though these features take a little extra time to put in place, they're worth the one-time effort — and you'll be glad you implemented them when they're the key to solving a puzzling problem.
Add Breadcrumbs
The more information you have on hand that details exactly what your users were doing leading up to an error, the easier and less frustrating it is to fix that error. By using breadcrumbs, you’ll never again need to rely on a user’s (usually incomplete) description of when and how the problem occurred.
You can record a breadcrumb at any point in your application’s lifecycle. The most recent 100 breadcrumbs are stored in memory, then sent to Sentry alongside any errors that occur.
Let’s say you’re a social photo-sharing app. You’d want to record breadcrumbs for every key action a user can take: upload a photo, select a filter, alter that filter in some way, choose any of the different edit tools, complete edits, add a location, tag someone, share a photo, back out of the process and save a draft.
Recording breadcrumbs is easy:
Sentry.getContext().recordBreadcrumb(
new BreadcrumbBuilder().setMessage("User 'Sally' began an image upload.").build()
);
Associate a user with the error
Tell Sentry who encountered the error. This can be a username from your app, an email address, or an IP — something that will uniquely tie the user to the error. As with breadcrumbs, this is stored locally in memory and sent along with any errors that occur.
This is valuable in two contexts:
From the developer side, you can see how many bugs this user has encountered and how many unique users a bug has impacted. If you have VIP users, you might even keep a special lookout for problems they’ve seen so that you can jump on those faster than you do for a typical error.
From the customer experience side, if a user reaches out to you about a problem, you won’t need to have them explain it since you can find them and immediately see what they’re talking about. Using tools like Sentry’s community-created Zendesk integration{:rel=”nofollow”}{:target=”_blank”} you can even access this info within your support system; enabling agents to immediately see what problem the user is talking about, so they can provide the best possible answer in any given situation.
Since we’re unlikely to guess correctly as to how you manage usernames, this is not something we can automatically add to your app. However, it is another easy-to-add one-liner. Here’s how it’s done:
Sentry.getContext().setUser(
new UserBuilder().setEmail("hello@sentry.io").build()
);
Users need only be described by one of: id
, username
, email
or ip
.
Add custom context with more tags and data fields
As noted at the very beginning of this post, we capture some basic phone state information (such as battery level and Android version) whenever a crash or error occurs. But there may be other pieces of information specific to your app that can help you get to the root of what’s causing a problem.
How fast was the phone moving? Maybe you notice an error occurs whenever a phone hits 88mph, indicating the problem may be interference from a user’s Flux Capacitor as it sends them backwards or forwards in time.
What subscription plan is the user on? Maybe a specific error only occurs for people paying for your super expensive enterprise plan.
Even in situations where an error impacts everyone, you could potentially use this data to determine which users to reach out to first with apologies and/or manual fixes.
Adding extra context and tags is very easy:
// tags only allow strings as values and are searchable/sortable in Sentry
Sentry.getContext().addTag("tagName", "tagValue");
// extra data values can be any type, even collections, and are displayed at
// the bottom of the Sentry error UI
Sentry.getContext().addExtra("extra", "thing");
Adding Profiling for Android
To integrate Sentry into your Android app, start by adding the Sentry Gradle plugin to your project. Use the following command to install Sentry’s CLI wizard:
npx @sentry/wizard -i android
The wizard automatically sets up Sentry in your project and includes performance monitoring for advanced debugging. To enable profiling, configure the SentryOptions in your app to capture profiling data:
SentryAndroid.init(context) { options ->
options.profilesSampleRate = 1.0 // Enables profiling for all sessions
}
With profiling enabled, Sentry will capture detailed performance insights and visualize them in a flame graph to help you identify bottlenecks. You can monitor slow functions and UI frame issues, enabling you to optimize your app’s performance effectively.
Adding session replay for android
To set up Android Session Replay with Sentry, first ensure you're using the latest version of the Sentry SDK. Then, enable session replay in your project by configuring the Sentry options. Add the following code to your initialization:
SentryAndroid.init(context) { options ->
options.enableSessionTracking = true
options.sessionTrackingIntervalMillis = 30000 // Adjust as needed
options.replaysSessionSampleRate = 1.0 // Captures 100% of sessions for replay
}
This will automatically capture user interactions and session data, making it easier to replay and debug issues from real user sessions.
Improved Android debugging with Sentry
One of the biggest challenges debugging crashes and errors on Android comes from the huge diversity of conditions and devices your app finds itself in. Handling some of the basics well, in addition to detecting and preventing ANRs will set you up for success. Taking it further with Sentry breadcrumbs, tags, profiling, and session replay, not only can you aggregate data in real time to fix bugs faster than before, you’ll also gain a proactive, broader view of how to build features that your most valuable users will love.
Android Debugging FAQs
You can use crash reporting tools like https://sentry.io/for/android/ to capture crashes, errors, and performance issues in real time during production. These tools provide reports with context, such as stack traces and user session replays, for efficient post-release debugging.
Logcat is ideal for local debugging with real-time logs, while production error monitoring tools provide real-time error tracking in live apps, along with context like stack traces and performance insights.
Integrating an error tracking SDK into your Android app enables automatic capturing of crashes, ANRs, and exceptions, generating detailed error reports in real-time for post-release debugging.
Use performance monitoring tools like Sentry for Android to track metrics like memory usage, CPU spikes, and slow frames. These tools provide insights into ANRs and other performance bottlenecks, helping to optimize app responsiveness and performance.
Android Debug Bridge (ADB) is a command-line tool that allows you to communicate with Android devices or emulators. It enables you to install apps, push or pull files, and perform actions like logging or shell commands. ADB is particularly useful for debugging apps by accessing device logs, inspecting file systems, or simulating network conditions, giving developers deep control over their Android environments.