Check out what’s new from Firebase@ Google I/O 2021, and join our alpha program for early access to the new Remote Config personalization feature. Learn more

Performance Monitoring of Feature Rollouts

In this codelab, you'll learn how to monitor your app's performance during a feature rollout. Our sample app will have basic functionality, and it's set up to display a different background image based on a Firebase Remote Config flag. We'll go over instrumenting traces to monitor the app's performance, rolling out a configuration change to the app, monitoring the effect and seeing how we can improve the performance.

What you'll learn

  • How to add Firebase Performance Monitoring to your mobile app to get out-of-the-box metrics (like app start time and slow or frozen frames)
  • How to add custom traces to understand critical code paths of your user journeys
  • How to use the Performance Monitoring dashboard to monitor your metrics and track important changes like the rollout of a feature
  • How to roll out a Firebase Remote Config change

Prerequisites

  • Android Studio 4.0 or higher
  • An Android emulator with API level 16 or higher.
  • Java version 8 or higher
  • A basic understanding of Firebase Remote Config

Download the code

Run the following command to clone the sample code for this codelab. This will create a folder called codelab-perf-rc-android on your machine:

$ git clone https://github.com/FirebaseExtended/codelab-feature-rollout-performance.git

If you don't have Git on your machine, you can also download the code directly from GitHub.

Import the project under the firebase-perf-rc-android-start folder into Android Studio. You will probably see some runtime exceptions or maybe a warning about a missing google-services.json file. We'll correct this in the next section.

In this codelab, we'll use the Firebase Assistant plugin to register our Android app with a Firebase project and add the necessary Firebase config files, plugins, and dependencies to our Android project — all from within Android Studio!

Connect your app to Firebase

  1. Go to Android Studio/Help > Check for updates to make sure that you're using the latest versions of Android Studio and the Firebase Assistant.
  2. Select Tools > Firebase to open the Assistant pane.

2fe37593d5b76c7.png

  1. Choose Performance Monitoring to add to your app, then click Get started with Performance Monitoring.
  2. Click Connect to Firebase to connect your Android project with Firebase (this will open up the Firebase console in your browser).
  3. In the Firebase console, click Add project, then enter a Firebase project name (if you already have a Firebase project, you can select that existing project instead). Click Continue and accept terms to create the Firebase project and a new Firebase App.
  1. You should next see a dialog to Connect your new Firebase App to your Android Studio project.

38e2c00493a8d4d4.png

  1. Back in Android Studio, in the Assistant pane, you should see the confirmation that your app is connected to Firebase.

dda8bdd9488167a0.png

Add Performance Monitoring to your app

In the Assistant pane in Android Studio, click Add Performance Monitoring to your app.

You should see a dialog to Accept Changes after which Android Studio should sync your app to ensure that all necessary dependencies have been added.

9b58145acc4be030.png

Finally, you should see the success message in the Assistant pane in Android Studio that all dependencies are set up correctly.

378356b65be49df8.png

As an additional step, enable debug logging by following the instructions in the step "(Optional) Enable debug logging". The same instructions are also available in the public documentation.

You should now see google-services.json file in the module (app-level) directory of your app, and your app should now compile. In Android Studio, click Run > Run ‘app' to build and run the app on your Android emulator.

When the app is running, you should first see a splash screen like this:

843a786fe3f42d0f.png

Then, after a few seconds, the main page with the default image will display:

e4b8dac61e405c61.png

What's happening under the hood?

The splash screen is implemented in SplashScreenActivity and does the following:

  1. In onCreate(), we initialize Firebase Remote Config settings and fetch the config values that you'll set in the Remote Config dashboard later in this codelab.
  2. In executeTasksBasedOnRC(), we read the config value of the seasonal_image_url flag. If a URL is provided by the config value, we download the image synchronously.
  3. Once the download is complete, the app navigates to MainActivity and calls finish() to end SplashScreenActivity.

In MainActivity, if seasonal_image_url is defined through Remote Config, the feature will be enabled and the downloaded image will be displayed as the background of the main page. Otherwise, the default image (shown above) will be displayed.

Now that our app is running, we can set up the new feature flag. In the left panel of the Firebase console, locate the Engage section, then click Remote Config.

In the Add a parameter card:

  1. Add seasonal_image_url as the parameter key.
  2. Click Add description, then enter this description: Shows a seasonal image (replaces default) in the main page when the restaurant list is empty.
  3. Click Add value for condition -> Define a new condition.
  4. For the condition name, enter Seasonal image rollout.
  5. For the Applies if... section, select User in random percentile <= 0%. (We want to leave the feature disabled until we're ready to roll out in a later step.)
  6. Click Create condition. We'll use this condition later to roll out the new feature to our users.

11547904ae42262d.png

  1. Back in the Add a parameter card, in the Value for Seasonal image rollout field, enter the URL from where to download the seasonal image: https://images.unsplash.com/photo-1552691021-7043334e0b51
  2. Leave the default value as an empty string. This means the default image in the codebase will be shown rather than an image downloaded from a URL.
  3. Click Add parameter. 45933b3f09e69849.png
  4. We can see that the new config was created as a draft. Click Publish changes and confirm the changes at the top to update our app.

38aa4cbe87bc58d3.png

Our app pre-loads some data prior to showing MainActivity and displays a splash screen to hide this process. We don't want our users to wait too long on this screen, so normally it's beneficial to monitor how long the splash screen is displayed.

Firebase Performance Monitoring provides a way to do just that. You can instrument custom code traces to monitor the performance of specific code in your app – like the loading time for data and the processing time of our new feature.

To track how long the splash screen is displayed, we'll add a custom code trace to SplashScreenActivity, which is the Activity that implements the splash screen.

  1. Initialize, create, and start a custom code trace named splash_screen_trace:

SplashScreenActivity.java

// ...
import com.google.firebase.perf.FirebasePerformance;
import com.google.firebase.perf.metrics.Trace;
// ...

public class SplashScreenActivity extends AppCompatActivity {

    private static final String TAG = "SplashScreenActivity";
    private static final String SEASONAL_IMAGE_URL_RC_FLAG = "seasonal_image_url";

    // TODO: Initialize splash_screen_trace
    private final Trace splashScreenTrace = FirebasePerformance.startTrace("splash_screen_trace");
    
    // ...
}
  1. End the trace in the onDestroy() method of SplashScreenActivity:

SplashScreenActivity.java

@Override
protected void onDestroy() {
    super.onDestroy();

    // TODO: Stop the splash_screen_trace here
    splashScreenTrace.stop();
}

Since our new feature downloads and processes an image, we'll add a second custom code trace that will track the additional time our feature has added to SplashScreenActivity.

  1. Initialize, create, and start a custom code trace named splash_seasonal_image_processing:

SplashScreenActivity.java

private void executeTasksBasedOnRC(FirebaseRemoteConfig rcConfig) {
    String seasonalImageUrl = rcConfig.getString(SEASONAL_IMAGE_URL_RC_FLAG);
    Log.d(TAG, SEASONAL_IMAGE_URL_RC_FLAG + ": " + seasonalImageUrl);

    if (!seasonalImageUrl.isEmpty()) {
        // TODO: Start the splash_seasonal_image_processing here
        final Trace seasonalImageProcessingTrace = FirebasePerformance
            .startTrace("splash_seasonal_image_processing");

        // ...
    }
}
  1. End the trace in the both onLoadFailed() and onResourceReady() methods of the RequestListener:

SplashScreenActivity.java

Glide.with(SplashScreenActivity.this.getApplicationContext())
    .asBitmap()
    .load(seasonalImageUrl)
    .signature(new ObjectKey(Utils.getCacheUUID()))
    .listener(new RequestListener<Bitmap>() {
        @Override
        public boolean onLoadFailed(
            @Nullable GlideException e,
            Object model, Target<Bitmap> target,
            boolean isFirstResource) {

            // TODO: Stop the splash_seasonal_image_processing here
            seasonalImageProcessingTrace.stop();

            launchMainActivity();
            return true;
        }

        @Override
        public boolean onResourceReady(Bitmap resource, Object model,
            Target<Bitmap> target, DataSource dataSource,
            boolean isFirstResource) {

            // TODO: Stop the splash_seasonal_image_processing here
            seasonalImageProcessingTrace.stop();

            launchMainActivity();
            return true;
        }
     })
     .preload();

Now that you've added custom code traces to track the splash screen duration (splash_screen_trace) and the processing time of the new feature (splash_seasonal_image_processing), run the app in Android Studio again. You should see a logging message that contains Logging trace metric: splash_screen_trace, followed by the duration of the trace. You won't see a log message for splash_seasonal_image_processing because we haven't enabled the new feature yet.

For custom code traces, Performance Monitoring automatically logs default attributes (common metadata like app version, country, device, etc.) so that you can filter the data for the trace in the Firebase console. You can also add and monitor custom attributes.

In our app, we've just added two custom code traces to monitor the splash screen duration and the processing time of the new feature. A factor that might affect these durations is whether the displayed image is the default image or if the image has to be downloaded from a URL. And who knows – we might eventually have different URLs from which we download an image.

So, let's add a custom attribute representing the seasonal image URL to these custom code traces. That way, we can filter duration data by these values later on.

  1. Add the custom attribute (seasonal_image_url_attribute) for splash_screen_trace in the beginning of the executeTasksBasedOnRC method:

SplashScreenActivity.java

private void executeTasksBasedOnRC(FirebaseRemoteConfig rcConfig) {
    String seasonalImageUrl = rcConfig.getString(SEASONAL_IMAGE_URL_RC_FLAG);
    Log.d(TAG, SEASONAL_IMAGE_URL_RC_FLAG + ": " + seasonalImageUrl);

    // TODO: Add a custom attribute "seasonal_image_url_attribute" to splash_screen_trace
    if (seasonalImageUrl.isEmpty()) {
        splashScreenTrace.putAttribute("seasonal_image_url_attribute", "unset");
    } else {
        splashScreenTrace.putAttribute("seasonal_image_url_attribute", seasonalImageUrl);
    }

    // ...
}
  1. Add the same custom attribute for splash_seasonal_image_processing right after the startTrace("splash_seasonal_image_processing") call:

SplashScreenActivity.java

if (!seasonalImageUrl.isEmpty()) {
    // TODO: Start the splash_seasonal_image_processing here
    final Trace seasonalImageProcessingTrace = FirebasePerformance
        .startTrace("splash_seasonal_image_processing");

    // TODO: Add a custom attribute "seasonal_image_url_attribute" to splash_seasonal_image_processing
    seasonalImageProcessingTrace
        .putAttribute("seasonal_image_url_attribute", seasonalImageUrl);

    // ...
}

Now that you've added a custom attribute (seasonal_image_url_attribute) for both of your custom traces (splash_screen_trace and splash_seasonal_image_processing), run the app in Android Studio again. You should see a logging message that contains Setting attribute 'seasonal_image_url_attribute' to 'unset' on trace 'splash_screen_trace'. We have not yet enabled the Remote Config parameter seasonalImageUrl which is why the attribute value is unset.

The Performance Monitoring SDK will collect the trace data and send them to Firebase. You can view the data in the Performance dashboard of the Firebase console, which we'll explain in detail in the next step of the codelab.

Configure your dashboard to monitor your feature

In the Firebase console, select the project that has your Friendly Eats app.

In the left panel, locate the Release & Monitor section, then click Performance.

You should see your Performance dashboard with your very first data points in your metrics board! The Performance Monitoring SDK collects performance data from your app and displays it within minutes of collection.

16d8b0db35956361.png

This metrics board is where you can track key metrics for your app. The default view includes the duration of your app start time trace, but you can add the metrics that you care about most. Since we're tracking the new feature that we added, we can tailor our dashboard to display the duration of the custom code trace splash_screen_trace.

  1. Click on one of the empty Select a metric boxes.
  2. In the dialog window, select the trace type of Custom traces and the trace name splash_screen_trace.

f3735b336c8c4b8.png

  1. Click Select metric, and you should see the duration of splash_screen_trace added to your dashboard!

You can use these same steps to add other metrics that you care about so that you can quickly see how their performance changes over time and even with different releases.

e7ac31d452e95dba.png

The metrics board is a powerful tool to track the performance of key metrics experienced by your users. For this codelab, we have a small set of data in a narrow time range, so we'll be using other dashboard views that will help us understand the performance of the feature rollout.

Now that we've set up our monitoring, we're ready to roll out the Firebase Remote Config change (seasonal_image_url) that we set up earlier.

To roll out a change, we'll go back to the Remote Config page in the Firebase console to increase the user percentile of our targeting condition. Normally, we would roll out new features to a small portion of the users and increase it only when we are confident that there are no issues with it. In this codelab, though, we're the only users of the app, so we can change the percentile to 100%.

  1. Click the Conditions tab at the top of the page.
  2. Click the Seasonal image rollout condition that you added earlier.
  3. Change the percentile to 100%.
  4. Click Save condition.
  5. Click Publish changes and confirm the changes.

83b527dd7918d288.png

Back in Android Studio, restart the app in your emulator to see the new feature. After the splash screen, you should see the new empty state main screen!

c4ad503bda164ab6.png

Now let's check out the performance of splash screen loading using the Performance dashboard in the Firebase console. In this step of the codelab, we'll use different parts of the dashboard to view performance data.

  1. On the main Dashboard tab, scroll down to the traces table, then click the Custom traces tab. In this table, you'll see the custom code traces we added earlier plus some out-of-the-box traces.
  2. Now that we have enabled the new feature, look for the custom code trace splash_seasonal_image_processing, which measured the time it took to download and process the image. From the trace's Duration value, we can see that this download and processing takes a significant amount of time.

ccb4e5762dfc4abb.png

  1. Since we have data for splash_seasonal_image_processing, we can add the duration of this trace to our metrics board at the top of the Dashboard tab.

Similar to before, click on one of the empty Select a metric boxes. In the dialog window, select the trace type Custom traces and the trace name splash_seasonal_image_processing. Finally, click Select metric to add this metric to the metrics board.

c0768ce8f147336a.png

  1. To further confirm the differences, we can take a closer look at the data for splash_screen_trace. Click on the splash_screen_trace card in the metrics board, then click View metric details.

637c29b428a4b98c.png

  1. In the details page, you'll see a list of attributes on the bottom left, including the custom attribute we created earlier. Click the custom attribute seasonal_image_url_attribute to view the splash screen duration for each seasonal image URL on the right:

f809ac2f7fc095fb.png

  1. Your splash screen duration values will probably be a bit different than those in the screenshot above, but you should have a longer duration when the image is downloaded from a URL versus using the default image (represented by "unset").

In this codelab, the reason for this longer duration might be straightforward, but in a real app, it may not be so obvious. The collected duration data will come from different devices, running the app in various network connection conditions, and these conditions could be worse than your expectation. Let's look at how you'd investigate this issue if this were a real world situation.

  1. Click Performance at the top of the page to go back to the Dashboard main tab: 103568148fde6aeb.png
  2. In the traces table at the bottom of the page, click the Network requests tab. In this table, you'll see all the network requests from your app aggregated into URL patterns, including the images.unsplash.com/** URL pattern. If you compare the value of this response time to the overall time it takes for image download and processing (i.e., the duration of the splash_seasonal_image_processing trace), we can see that a large amount of the time is spent on downloading the image.

d629934fc7ad4903.png

Performance findings

Using Firebase Performance Monitoring, we saw the following impact on the end users with the new feature enabled:

  1. The time spent on SplashScreenActivity has increased.
  2. The duration for splash_seasonal_image_processing was very large.
  3. The delay was due to the response time for the image download and the corresponding processing time needed for the image.

In the next step, we'll mitigate the impact on performance by rolling back the feature and identifying how we can improve the implementation of the feature.

Increasing our users' wait time during the splash screen is not desirable. One of the key benefits of Remote Config is the ability to pause and reverse our rollout without having to release another version to our users. This allows us to quickly react to issues (like the performance issues that we discovered in the last step) and minimize the number of unhappy users.

As a fast mitigation, we'll reset the rollout percentile back to 0 so that all our users will see the default image again:

  1. Go back to the Remote Config page in the Firebase console.
  2. Click on Conditions at the top of the page.
  3. Click on the Seasonal image rollout condition you added earlier.
  4. Change the percentile to 0%.
  5. Click Save condition.
  6. Click Publish changes and confirm the changes.

9ed5808f15d2432.png

Restart the app in Android Studio, and you should see the original empty state main screen:

e4b8dac61e405c61.png

We discovered earlier in the codelab that downloading an image for our splash screen was causing the slowdown for our app. Taking a closer look at the downloaded image, we see that we're using the original resolution of the image, which was over 2MB! One quick fix for our performance issue is to reduce the quality to a more appropriate resolution so that the image takes less time to download.

Roll out the Remote Config value again

  1. Go back to the Remote Config page in the Firebase console.
  2. Click the Edit icon for the seasonal_image_url parameter.
  3. Update the Value for Seasonal image rollout to https://images.unsplash.com/photo-1552691021-7043334e0b51?w=640, then click Update.

79ff14f179636316.png

  1. Click on the Conditions tab at the top of the page.
  2. Click on Seasonal image rollout, then set the percentile back to 100%.
  3. Click Save condition.

4578d491f02708a5.png

  1. Click on the Publish changes button.

Run the app locally

With the new config value set to use a different download image URL, run the app again. This time, you should notice that the time spent on the splash screen is shorter than before.

c4ad503bda164ab6.png

View the performance of the changes

We can go back to the Performance dashboard in the Firebase console to see how our metrics look.

  1. This time we'll use the traces table to navigate into the details page. Down in the traces table, in the Custom traces tab, click the custom trace splash_seasonal_image_processing to see a more detailed view of its duration metric again.

71f13ad560bbddba.png

  1. Click the custom attribute seasonal_image_url_attribute to see the breakdown of the custom attributes again. If you hover over the URLs, you will see one that matches the new URL for the reduced-size image: https://images.unsplash.com/photo-1552691021-7043334e0b51?w=640 (with the ?w=640 at the end). The duration value associated with this image is considerably shorter than the value for the previous image and more acceptable for our users.

e522b4f0f315ce3f.png

Congratulations! You've enabled the Firebase Performance Monitoring SDK and collected traces to measure the performance of a new feature! You were able to monitor key performance metrics for the rollout of a new feature and react quickly when a performance issue was discovered. This was all possible with the ability to make config changes with Remote Config and monitor for performance issues in real time.

What we've covered?

  • Adding the Firebase Performance Monitoring SDK to your app
  • Adding a custom code trace to your code to measure a specific feature
  • Setting up a Remote Config parameter and conditional value to control/rollout a new feature
  • Understanding how to use the performance monitoring dashboard to identify issues during a rollout

Learn more