Home Android & Kotlin Books Android Debugging by Tutorials

4
Analyzing the Stack Trace Written by Zac Lippard

Heads up... You're reading this book for free, with parts of this chapter shown beyond this point as scrambled text.

You can unlock the rest of this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.

In the previous chapter, you learned about Logcat and how you can use the Logcat window to find bugs in your app. One common example of Logcat is finding stack traces for crashes and exceptions.

Analyzing a stack trace, and understanding how the trace itself is structured, provides clues as to why the app encountered the error, to begin with.

In this chapter, you’ll learn how to read a stack trace from the Podplay app, use tools to navigate through it and fix the associated bug behind the trace. You’ll be able to:

  • Read through a stack trace.
  • Catch errors and rethrow them with more information.
  • View the associated threads in a stack trace.
  • Add Firebase Crashlytics to your app.
  • Import a Crashlytics stack trace and fix the underlying error.

Defining Stack Trace

A stack trace shows a list of methods called at a certain place in your code. This list is typically referred to as the stack frame or call stack. In Android development, an exception generates a stack trace. You can review the stack trace to determine the underlying cause of a bug in your app.

For example, you may have the following methods, a(), b() and c():

fun a() {
  b()
}

fun b() {
  c()
}

fun c() {
  throw Exception("Uh oh!")
}

When the exception throws, the stack trace will contain the methods with the most recent method call at the top:

c()
b()
a()

Stack traces in Android will also include line numbers to denote where the next function call is in the stack.

Next, you’ll learn how to read and utilize stack traces to fix a bug in the Podplay app.

Reading a Stack Trace

Open the Podplay starter project and run the app. After the app launches, tap the search icon, type in “sermon audio” and press Return.

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.yourcompany.podplay, PID: 22519
    java.text.ParseException: Unparseable date: "Fri, 11 Feb 2022 02:10 GMT"
        at java.text.DateFormat.parse(DateFormat.java:362)
        at com.yourcompany.podplay.util.DateUtils.xmlDateToDate(DateUtils.kt:56)
        at com.yourcompany.podplay.repository.PodcastRepo.rssItemsToEpisodes(PodcastRepo.kt:75)
        at com.yourcompany.podplay.repository.PodcastRepo.rssResponseToPodcast(PodcastRepo.kt:88)
        at com.yourcompany.podplay.repository.PodcastRepo.getPodcast(PodcastRepo.kt:61)
        at com.yourcompany.podplay.repository.PodcastRepo$getPodcast$1.invokeSuspend(Unknown Source:15)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
        at android.os.Handler.handleCallback(Handler.java:883)
        at android.os.Handler.dispatchMessage(Handler.java:100)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:7356)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)

Catching and Rethrowing Errors

In many cases, the best way to prevent crashes from thrown exceptions is to catch the exception in a try/catch block. The try block will run your code, and the catch block will catch the exception types you specify. You can use this to handle the crash above.

return inFormat.parse(date) ?: Date()

return try {
  inFormat.parse(date) ?: Date()
} catch (e: ParseException) {
  Log.wtf("xmlDateToDate", e)
  Date()
}
2022-05-18 22:37:44.092 22747-22747/com.yourcompany.podplay E/xmlDateToDate: Unparseable date: "Thu, 10 Feb 2022 01:25 GMT"
    java.text.ParseException: Unparseable date: "Thu, 10 Feb 2022 01:25 GMT"
        at java.text.DateFormat.parse(DateFormat.java:362)
        at com.yourcompany.podplay.util.DateUtils.xmlDateToDate(DateUtils.kt:59)
        at com.yourcompany.podplay.repository.PodcastRepo.rssItemsToEpisodes(PodcastRepo.kt:75)
        at com.yourcompany.podplay.repository.PodcastRepo.rssResponseToPodcast(PodcastRepo.kt:88)
        at com.yourcompany.podplay.repository.PodcastRepo.getPodcast(PodcastRepo.kt:61)
        at com.yourcompany.podplay.repository.PodcastRepo$getPodcast$1.invokeSuspend(Unknown Source:15)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
        at android.os.Handler.handleCallback(Handler.java:883)
        at android.os.Handler.dispatchMessage(Handler.java:100)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:7356)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)

Firebase Crashlytics

Crashlytics is an invaluable tool that provides you with crash reports, non-fatal errors, and Application Not Responding (ANR) errors. Firebase is Google’s suggested app development platform which hosts Crashlytics as a service, along with several other great tools.

Creating a Firebase Project

To start, go to the Firebase Console and set up a new Firebase project. Click Add project.

Adding Podplay to the Firebase Project

You can add the Podplay app to the Firebase project from the dashboard. Click the Android icon to start the process of adding Podplay.

keytool -list -v \
-alias androiddebugkey -keystore .android/debug.keystore

classpath 'com.google.gms:google-services:4.3.10'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.0'
apply plugin: 'com.google.gms.google-services'
apply plugin: 'com.google.firebase.crashlytics'
implementation platform('com.google.firebase:firebase-bom:30.0.1')
implementation 'com.google.firebase:firebase-crashlytics-ktx'
implementation 'com.google.firebase:firebase-analytics-ktx'

Previewing Crashes Within Crashlytics

From the Firebase project dashboard, select the Crashlytics option under Release & Monitor on the left-hand column.

throw RuntimeException("Test for Crashlytics!")

Analyzing External Stack Traces

Now that you’ve got a stack trace from Crashlytics, it’s time to incorporate that with Android Studio to fix the bug you created!

Challenge

It’s time to put what you’ve learned to use. There’s another bug that is present in Podplay. Launch the app and search for any podcast. When the list of podcasts displays, repeatedly tap one until the app crashes.

Key Points

  • A stack trace shows a list of methods called at a certain place in your code.
  • Understand the method calls that led up to the crash by reading a stack trace.
  • You can capture crashes and rethrow them as non-fatal errors.
  • Use Firebase console to connect Crashlytics to any Android app.
  • You can utilize Crashlytics to monitor crashes and find stack traces.

Where to Go From Here?

Firebase Crashlytics is a powerful tool that you have at your disposal. You can find more in-depth details in the Firebase Crashlytics documentation about the Crashlytics service and what you can do with it.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.

© 2022 Razeware LLC

You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.

Unlock Now

To highlight or take notes, you’ll need to own this book in a subscription or purchased by itself.