Chapters

Hide chapters

Real-World Android by Tutorials

First Edition · Android 10 · Kotlin 1.4 · AS 4

Section I: Developing Real World Apps

Section 1: 7 chapters
Show chapters Hide chapters

19. Firebase Integration
Written by Subhrajyoti Sen

Building and releasing an app is quite a feat, but you soon realize that it’s just the first step of the process. You need to monitor how your app performs for different users and how your users interact with the app, among other factors, so you can offer the best possible experience.

Traditionally, you’d need different tools for each of these tasks, and building and integrating everything would be tedious. Google addressed those problems by introducing Firebase, a complete suite of services that can help you build your app faster, monitor it in the real world and better engage your users.

In this section, you’ll learn how to use:

  1. The Firebase Console to set up Firebase for your project.
  2. Crashlytics to detect and understand app crashes.
  3. Remote Config to add dynamic content to your app.
  4. Test Lab to perform different tests across a wide range of devices.

You’ll start at the beginning: getting Firebase ready to use.

Setting up Firebase

To set up Firebase, you first need to create a new project at https://console.firebase.google.com. Log in using a Google Account and you’ll see the Firebase Console. Firebase will prompt you to create a new project, as shown below:

Figure 19.1 — Creating a Firebase Project
Figure 19.1 — Creating a Firebase Project

Creating a Firebase project

Clicking Create a project will bring you to the Create a project page. You’ll see a prompt to provide a project name, as shown below. Enter PetSave.

Figure 19.2 — Insert Firebase Project Name
Figure 19.2 — Insert Firebase Project Name

Click Continue. Next, you’ll get an option to enable Google Analytics for your project. Disable Google Analytics, since you won’t use it in this project.

Figure 19.3 — Google Analytics Configuration
Figure 19.3 — Google Analytics Configuration

Once done, click Create Project and Firebase will get to work. When it finishes creating your project, click Continue.

Registering an app

Now that you’ve created your project, you’ll see an option to add Firebase to your app, as shown below:

Figure 19.4 — Adding Firebase to Your App
Figure 19.4 — Adding Firebase to Your App

Click the Android icon and the Add Firebase to your Android app page will appear. First, you need to add the package name of your app. For this project, the package name is com.raywenderlich.android.petsave.

Note: You can find the package name in the app build.gradle, as the applicationId value.

Skip the next two input fields; you won’t need them for this chapter.

Figure 19.5 — Register Your App
Figure 19.5 — Register Your App

Click Register app. Next, click Download google-services.json and move the downloaded file to the project root directory.

Finally, you need to add the Firebase plugin and dependency. Open the project build.gradle and add the following line to the dependencies block:

classpath 'com.google.gms:google-services:4.3.4'

The code above adds the Google Services plugin.

Now, add the following line to the app build.gradle, right above the android block:

apply plugin: 'com.google.gms.google-services'

This enables the plugin.

Finally, you need to add the following dependency inside the dependencies block in the same file:

implementation platform('com.google.firebase:firebase-bom:26.2.0')

Click Sync now to download the dependencies, then build the project to make sure the dependencies haven’t caused any issues.

Back in the Firebase Console, click Next, then Continue to console. And that’s it. You’ve successfully added Firebase to your project.

Crashlytics

App crashes are among the things developers dread the most. Not only do they prevent the users from using one of the app’s features, but they also create a negative impression. Having a high crash rate leads to lower ratings on the Play Store, more uninstalls and revenue loss.

It’s crucial to be able to detect and fix crashes on user devices. Crashlytics is one of the most popular services when it comes to crash reporting. Best of all, it’s simple to configure.

Setting up Crashlytics

Setting up Crashlytics is straightforward. Select Crashlytics from the left navigation bar on the Firebase Console and you’ll see a page like the one below:

Figure 19.6 — Enabling Crashlytics
Figure 19.6 — Enabling Crashlytics

Click Enable Crashlytics. Now, head to Android Studio and add the following Gradle plugin to the dependencies block of the project build.gradle:

classpath 'com.google.firebase:firebase-crashlytics-gradle:2.4.1'

Next, apply the Crashlytics Gradle plugin by adding the following line to the app build.gradle:

apply plugin: 'com.google.firebase.crashlytics'

Finally, add the following dependencies in the app build.gradle:

implementation 'com.google.firebase:firebase-crashlytics-ktx'
implementation 'com.google.firebase:firebase-analytics-ktx'

Click Sync now to download the dependencies and… that’s it! The setup for Crashlytics is complete. Crashlytics’ SDK uses content providers to auto-initialize on app startup. Therefore, you don’t need to add any initialization code.

Testing and debugging

To test your Crashlytics setup, you need to cause an intentional crash. Do this by opening AnimalsNearYouFragment.kt and adding the following code to onViewCreated:

throw NullPointerException()

The code above adds an unhandled exception that makes the app crash.

Build and run the project, and you’ll see the app crash soon after launching. Try opening the app again to verify the crash.

Next, head over to the Crashlytics page on Firebase Console and refresh the page. You’ll now be able to view the crashes and the stack trace for each crash, like this:

Figure 19.7 — Simulating a Crash
Figure 19.7 — Simulating a Crash

From now on, whenever your app crashes, Crashlytics will upload a report to Firebase along with the stack traces. If you don’t see any logged crashes, revisit the page after a few minutes; Crashlytics sometimes takes a while to upload the data.

Non-fatal exceptions

You can also use Firebase to log non-fatal exceptions. In most cases, you log such exceptions locally. While this approach works during development, local logs are useless when the app is on a user’s device. Instead, you’ll log them in Crashlytics.

In onViewCreated of AnimalsNearYouFragment.kt, wrap the unhandled exception in a try-catch block, as shown below:

try {
  throw NullPointerException()
} catch (exception: Exception) {
  FirebaseCrashlytics.getInstance().recordException(exception)
}

In the code above, you use FirebaseCrashlytics.getInstance().recordException to log the exception to Crashlytics.

Build and run. The app will no longer crash, but it will log the exception. To view the exception on Firebase, go to the Crashlytics page on Firebase Console and click Filter ▸ Event type ▸ Non-fatals, as shown below:

Figure 19.8 — Filtering a Non-Fatal Exception With Crashlytics
Figure 19.8 — Filtering a Non-Fatal Exception With Crashlytics

You can view the exception now. If the exception doesn’t appear on the page, check back after some time; to optimize CPU and battery usage, Firebase uploads non-fatal data in batches.

Using Crashlytics with Proguard

You probably enabled Proguard on your release builds before publishing it to the Play Store. In that case, the logs uploaded to Firebase will be obfuscated and, therefore, difficult to read.

You can enable Proguard on your debug builds to ensure that Proguard itself didn’t introduce any crashes. Open the app build.gradle and add the following code to the buildTypes block:

debug {
  minifyEnabled true
  proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}

The code above enables Proguard on the debug variant and also uses proguard-rules.pro to get the Proguard rules defined by the developer.

Open AnimalsNearYouFragment.kt and remove the try-catch block around NullPointerException so it becomes a fatal exception.

To make sure that Firebase can provide correct line numbers and source files in the crash report, you need to add the following line to proguard-rules.pro:

-keepattributes SourceFile,LineNumberTable

The Proguard rule above prevents the line number and source file from getting obfuscated.

Build and run. As expected, the app will crash. Head to the Crashlytics dashboard and verify that the stack trace isn’t obfuscated.

Uploading the mapping file

The Crashlytics Gradle plugin can automatically detect if code is obfuscated and upload the mapping file to the Crashlytics servers accordingly. Though this process is handy, it slows down build times.

When developing locally, you can use Logcat instead of Crashlytics to debug crashes. Therefore, you’ll disable uploading the mapping file on debug builds.

Open the app build.gradle and add the following code to the debug build variant:

firebaseCrashlytics {
  mappingFileUploadEnabled false
}

The debug variant will be similar to the one shown below:

debug {
  minifyEnabled true
  proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
  firebaseCrashlytics {
    mappingFileUploadEnabled false
  }
}

To verify that the mapping file isn’t uploading, build and run. A few minutes after the build crashes, go to the Crashlytics dashboard and look at the stack trace for the crash. You’ll notice that it’s obfuscated.

Finally, remember to remove the unhandled exception from AnimalsNearYouFragment.kt so the app doesn’t keep crashing.

Remote Config

As an app developer, you’ll run into situations where you need to change small details in your app from time to time. Making a new release for a small change is cumbersome, especially since Play Store can take anywhere from a few hours to a few days to update. For these cases, Firebase provides Remote Config.

Remote Config is a set of key-value pairs stored on the cloud. Your app can fetch them and cache them locally on the device after a fixed duration.

Consider the secret pet image you show the user when they drag the Call button onto the pet’s image. At the moment, you don’t have a way of changing this image without releasing a new update. Next, you’ll use Remote Config to dynamically change the image URL for the secret pet.

Setting up Remote Config

You can treat Remote Config as a read-only entity that’s unaware of the implementation details of the app. You can also treat it as a source of key-value pairs.

Create a new Android module by clicking File ▸ New module ▸ Android library. Give it the name remoteconfig and choose a Bytecode Level of 7.

Once you have the module, add the following dependencies to build.gradle in the remoteconfig module:

implementation platform('com.google.firebase:firebase-bom:26.1.1')
implementation 'com.google.firebase:firebase-config-ktx'

The dependencies above add the Firebase Remote Config SDK. Click Sync now and wait for the dependencies to sync.

Next, create a new file named RemoteConfigUtil.kt in the remoteconfig module and add the following code:

object RemoteConfigUtil {

  private val DEFAULTS: HashMap<String, Any> = hashMapOf()

  private lateinit var remoteConfig: FirebaseRemoteConfig

  fun init(debug: Boolean = false) {
    remoteConfig = getFirebaseRemoteConfig(debug)
  }

  private fun getFirebaseRemoteConfig(debug: Boolean): FirebaseRemoteConfig {

    val remoteConfig = Firebase.remoteConfig

    val configSettings = remoteConfigSettings {
      if (debug) {
        minimumFetchIntervalInSeconds = 0
      } else {
        minimumFetchIntervalInSeconds = 60 * 60
      }
    }

    remoteConfig.setConfigSettingsAsync(configSettings)
    remoteConfig.setDefaultsAsync(DEFAULTS)
    remoteConfig.fetchAndActivate()

    return remoteConfig
  }
}

The code above does the following:

  1. Serves as a utility singleton class to set the Remote Config configuration.

  2. minimumFetchIntervalInSeconds specifies the cache interval. If the elapsed time since the last fetch is less than the cache interval, the SDK will use the cached values. Else, it will fetch the latest values. During debugging, it’s useful to set the duration to 0 seconds to always get the latest values.

  3. DEFAULTS is a HashMap that specifies the default values for the different Remote Config keys. You’ll use the default values until you set new values in the Firebase Remote Config dashboard.

  4. fetchAndActivate fetches the latest values and activates them. If you only call fetch(), the values will only be available to your app in the next user session.

Remote Config values are fetched asynchronously, so you should be careful about how you handle updated values. You don’t want scenarios where the app’s behavior changes while the user is in the app.

Consider a case where a user is on an order confirmation page and there are two buttons named Cancel and Order. The user decides to cancel the order, but just as they are about to tap Cancel, a Remote Config value swaps the positions of the button. The user will understandably be very annoyed. In some cases, it’s acceptable for the Remote Config changes to appear in the app in a later session.

To initialize Remote Config using this helper class, first add the remoteconfig module as a dependency to the app module. Open the app build.gradle and add the following line of code to the dependencies block:

implementation project(":remoteconfig")

Next, open PetSaveApplication.kt and add the following code inside onCreate:

RemoteConfigUtil.init(BuildConfig.DEBUG)

The code above calls the init of the helper class and passes a parameter indicating whether the current build is a debug build.

Adding a config

Open fragment_secrets.xml. You’ll notice an android:src attribute specifying the image to display. Remove the attribute and add an id to the ImageView, as follows:

<ImageView
  android:id="@+id/secret_image"
  android:layout_width="match_parent"
  android:layout_height="match_parent" />

Now, you’ll use Remote Config to get the URL of the image to display. Open RemoteConfigUtil.kt and add the following member variable to it:

private const val SECRET_IMAGE_URL = "secret_image_url"

secret_image_url will serve as the key for the image URL config.

Since you’ve added a new key, you also have to provide a new default value for it. Modify DEFAULTS, as shown below:

private val DEFAULTS: HashMap<String, Any> =
  hashMapOf(
      SECRET_IMAGE_URL to "https://images.pexels.com/photos/1108099/pexels-photo-1108099.jpeg"
  )

In the code value, you set a default value for the secret_image_url key.

You also need to provide a getter method so the Pet Details page can access the value. Add the following method to the same file:

fun getSecretImageUrl() = remoteConfig.getString(SECRET_IMAGE_URL)

In the code above, you use getString() to get the value from Remote Config using the key. There are similar methods for other types, like getBoolean(), getLong(), etc. You store the values as strings on Remote Config. It’s your responsibility to call the appropriate function to cast the value properly.

Using a dynamic value to update the UI

The only thing left to do on the app side is updating SecretFragment.kt to use the updated value and setting the image in the ImageView.

Open SecretFragment.kt and add the following method:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
  super.onViewCreated(view, savedInstanceState)

  binding.secretImage.setImage(RemoteConfigUtil.getSecretImageUrl())
}

In this code, you call the extension function setImage() and pass the value from Remote Config as a parameter.

Build and run. Go to the details screen for any pet and flick the Call button onto the pet’s image. The secret screen will appear — and it will now have the image you set as the default Remote Config value!

Figure 19.9 — Testing Remote Config
Figure 19.9 — Testing Remote Config

Updating the Remote Config value

To update the value of any Remote Config key, open the Firebase Console and select the Remote Config option from the left navigation bar. It’s in the Engagement section.

On the Remote Config dashboard, click Add Parameter. You’ll get a dialog like the one below:

Figure 19.10 — Add a New Parameter to Remote Config
Figure 19.10 — Add a New Parameter to Remote Config

For the Parameter key, use the same key you used in the codebase. In this case, it’s secret_image_url. For the Default value, add the URL of the image you want to change to. For this project, use https://images.pexels.com/photos/2253275/pexels-photo-2253275.jpeg, which contains a picture of a cute dog. Click Add parameter.

You’ll see a banner at the top that tells you that changes have been made, but they’re not published yet.

Figure 19.11 — Unpublish Changes
Figure 19.11 — Unpublish Changes

Click Publish changes, then click Publish changes again on the confirmation pop-up. The updated values will be available immediately.

Close and open the app, then use it for about 10 to 20 seconds so the Remote Config SDK has enough time to fetch the new value. Now, go to the details screen and unlock the secret screen. The screen will now have the updated image from Remote Config.

Figure 19.12 — Testing Configuration Changes With Remote Config
Figure 19.12 — Testing Configuration Changes With Remote Config

Congratulations, you’ve successfully used Remote Config to set up a feature that you can control without having to push any new updates!

Firebase Test Lab

Android is a highly fragmented operating system. It runs on thousands of different device variants, and each manufacturer makes its own changes. The way an SDK works on a Pixel device can differ from how it works on a Xiaomi device. Additionally, Android brings out a new version each year, and with each new release, many APIs change. Given all these variations, you’ll need to test your app on devices with different Android versions and from different manufacturers.

Unless you have access to a wide collection of mobile test devices, such a testing approach is quite difficult, for three reasons:

  1. Cost: It’s expensive to buy so many devices.
  2. Availability: Many devices disappear from the market after just one or two years. Trying to procure a used device might be your only option.
  3. Management: It’s quite a task to test your app on each device.

Fortunately, Firebase introduced Test Lab , which automates this entire process at a lower cost. It lets you choose a set of devices from different manufacturers, upload your app, then test it on all the selected devices.

Running your first test

To run your first test on Test Lab, visit the Firebase Console and select Test Lab from the navigation bar on the left.

You’ll get a set of options, as shown below:

Figure 19.13 — Getting Started With Test Lab
Figure 19.13 — Getting Started With Test Lab

You can use both debug and release APKs on Test Lab. Generate a debug APK and upload it to the Android Robo test option. In a Robo test, a crawler goes through different screens on your app and interacts with different UI elements. The crawler records all the interactions and takes screenshots along the way.

Once you’ve uploaded the APK, Test Lab will start running a Robo test on a Pixel device at API Level 26 using English (United States) locale and Portrait orientation.

Figure 19.14 — Test Lab Default Test Matrix
Figure 19.14 — Test Lab Default Test Matrix

This is the default configuration that Test Lab uses. A combination of test devices, API levels, locales and orientations is called a test matrix. You can create a new matrix to suit your requirements.

The test will take a few minutes to complete. Once it’s done, you’ll receive an email with the test report. A Robo test fails if the app crashes during the test. When you open the test results, you’ll get details of the test, like:

  • The time the test took.
  • The number of actions the crawler performed.
  • A crawl graph along with screenshots to demonstrate the different paths the crawler took.
  • A video of all the interactions.
  • Logs produced on the device during the test.
  • CPU, memory and network performance statistics.
  • Accessibility issues, warnings and suggestions.

Creating a Robo test preset

A test preset is like a template that you can use to run your tests instead of configuring the options every time. A preset consists of the following:

  1. Name
  2. Description
  3. Test type: Robo test, instrumentation test or game loop
  4. The set of devices to use
  5. Additional options, depending on the test type

To create a new preset, select Presets on the Test Lab page. Since this is your first preset, you’ll see a page like the one below:

Figure 19.15 — Test Lab New Preset
Figure 19.15 — Test Lab New Preset

Click Create a new preset and you’ll come to the New Preset page. Give the preset any name and description you want.

In the test type, select Robo test. You’ll create an Instrumentation test later.

Since you haven’t created a test matrix before, you’ll see a section like the one below:

Figure 19.16 — Customize Your Test Matrix
Figure 19.16 — Customize Your Test Matrix

Click Customize to proceed to the Customize device selection screen, where you can choose from a huge list of devices. Test Lab has two types of devices:

  1. Virtual: Emulators that run on Google Cloud Platform.
  2. Physical: Actual devices that are stored at Google data centers.

The number and types of devices you can run on your tests depends on your Firebase plan. Physical devices are much more expensive than virtual ones.

Once you select a device, you can choose from the available API levels, locales and orientations. You can add the same device multiple times with different configurations. For now, add a Pixel 2 and a Redmi 6 Pro with English (United States) locale and Portrait orientation. Once you’re done, click Confirm.

Next, expand the Additional option section. You’ll get the following options:

  • Test timeout: How long you want the test to run before stopping it. This comes in handy if you’re paying for Test Lab by the minute.
  • Test account credentials: If your app implements a custom login screen, you can add the login credentials along with the ID of the EditTexts to input them during the test.
  • Robo directives: You can specify the behavior of the crawler when it encounters a resource with a specific name.
  • Deep links: You can specify up to three deep links. The crawler will open the app using these links and crawl them for 30 seconds each.

Once you’ve entered your desired values, click Save preset.

Creating an instrumentation test preset

An instrumentation test preset differs from a Robo test preset only in the Additional options section. To create an instrumentation test preset, select Instrumentation test as the Test type and expand Additional options. There are three options in this category:

  1. Test timeout: This option is the same as the one for the Robo test.
  2. Android Test Orchestrator: Using Orchestrator insulates your tests so crashes and state changes in one test don’t affect others.
  3. Sharding: This allows you to run your tests in parallel by grouping them into different sets, thus speeding up the test suite. Each shard counts as a new device, so depending on your Firebase plan, you might need to use this option cautiously.

Running a new test

To run a new test, visit the Test Lab dashboard and click Run a test. Select the type of test you want from the drop-down menu.

When you select a Robo test, you have to upload an APK — either a debug APK or a release APK. You can also upload a Robo script to direct the crawler.

Figure 19.17 — Robo Script Configuration
Figure 19.17 — Robo Script Configuration

To create a new Robo script, open Android Studio and go to Tools ▸ Firebase. If the Firebase option is missing, make sure you’ve enabled Firebase Services and Firebase Testing in Preferences ▸ Plugins ▸ Installed.

Once the Firebase panel loads, select Test Lab ▸ Record Robo Script and use it to Guide Robo Test, then follow the steps displayed on the screen.

When you select an Instrumentation test, you have to upload a regular APK and a test APK. To generate a test APK, run the following command in the Terminal tab in Android Studio:

./gradlew assembleAndroidTest

This generates an APK that includes your instrumentation tests. You can find it in app/build/outputs/apk/androidTest/debug.

Once you’ve uploaded the APKs, click Continue, then choose between creating a new device set or using a preset. Once you’ve chosen a set, click Run 2 tests. Here, 2 signifies that you’re running tests on two devices with a sharding of 1. You’ll receive an email when the testing completes.

That’s all there is to it! You now have thousands of devices at your disposal for testing, and you didn’t even have to visit the store. :]

Key points

  • Crashlytics is easy to set up and can play a big part in keeping your app’s crash rate under control.
  • Use Crashlytics to log non-fatal exceptions.
  • Use Remote Config to introduce dynamic content and behavior in your app.
  • Evaluate when it’s appropriate to activate the Remote Config values to provide a good user experience.
  • Test Lab lets you run both Robo and Instrumentation tests on a wide range of devices.
  • Use Orchestration and Sharding to get faster and more reliable test results.
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.
© 2026 Kodeco Inc.