Measure load time and screen rendering with Firebase Performance Monitoring

1. Introduction

Last Updated:2021-03-11

Why do we need to measure the performance of Views?

Views are a key part of Android applications that directly affect the user experience. For example, your Activity or Fragment contains the UI which holds the View components that users interact with. Users are not able to see the entire content of the UI until it's completely drawn on the screen. Slow and frozen screens will directly impair user interaction with your app and create a bad user experience.

Doesn't Firebase Performance Monitoring provide these performance metrics out-of-the-box?

Firebase Performance Monitoring automatically captures some performance data out-of-the-box, such as your app start time (i.e., the load time for your first Activity only) and screen rendering performance (i.e., slow and frozen frames for Activities but not for Fragments). However, industry apps usually don't have lots of Activities but rather one Activity and multiple Fragments. Also, many apps usually implement their own Custom Views for more complex use cases. So it's often useful to understand how to measure the load time and screen rendering performance of both Activities and Fragments by instrumenting custom code traces in your app. You can easily extend this codelab to measure performance of Custom View components.

What you'll learn

  • How to add Firebase Performance Monitoring to an Android app
  • Understanding the loading of an Activity or a Fragment
  • How to instrument custom code traces to measure the load time of an Activity or Fragment
  • Understanding Screen Rendering and what is a Slow/Frozen frame
  • How to instrument custom code traces with metrics to record Slow/Frozen screens
  • How to view the collected metrics in the Firebase console

What you'll need

  • Android Studio 4.0 or higher
  • An Android device/emulator
  • Java version 8 or higher

2. Getting set up

Get the code

Run the following commands to clone the sample code for this codelab. This will create a folder called codelab-measure-android-view-performance on your machine:

 $  
git  
clone  
https://github.com/FirebaseExtended/codelab-measure-android-view-performance.git
$  
 cd 
  
codelab-measure-android-view-performance 

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

Import the measure-view-performance-start project into Android Studio. You will probably see some compilation errors or maybe a warning about a missing google-services.json file. We'll correct this in the next section of this step.

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 updatesto make sure that you're using the latest versions of Android Studio and the Firebase Assistant.
  2. Select Tools> Firebaseto open the Assistant pane.
    e791bed0999db1e0.png
  3. Choose Performance Monitoringto add to your app, then click Get started with Performance Monitoring.
  4. Click the button to create a new project, and then enter a project name (for example, Measure Performance Codelab ).
  5. Click Continue.
  6. If prompted, review and accept the Firebase terms , and then click Continue.
  7. (Optional) Enable AI assistance in the Firebase console (called "Gemini in Firebase").
  8. For this codelab, you do not need Google Analytics, so toggle off the Google Analytics option.
  9. You should next see a dialog to Connectyour new Firebase App to your Android Studio project.
    42c498d28ead2b77.png
  10. 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 Changesafter 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.

aa0d46fc944e0c0b.png

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

3. Run the app

If you have successfully integrated your app with the Performance Monitoring SDK, the project should now compile. In Android Studio, click Run> Run ‘app'to build and run the app on your connected Android device/emulator.

The app has two buttons that take you to a corresponding Activity and Fragment, like this:

410d8686b4f45c33.png

In the following steps of this codelab, you'll learn how to measure the load time and screen rendering performance of your Activity or Fragment.

4. Understanding the loading of an Activity or Fragment

In this step, we will learn what the system is doing during the loading of an Activity or Fragment.

Understanding the loading of an Activity

For an Activity, the load time is defined as the time starting from when the Activity object is created all the way until the First Frameis completely drawn on the screen ( this is when your user will see the complete UI for the Activity for the first time ). To measure if your app is fully drawn, you can use the reportFullyDrawn() method to measure the elapsed time between application launch and complete display of all resources and view hierarchies.

On a high level, when your app calls startActivity(Intent) , the system automatically performs the following processes. Each process takes time to complete, which adds to the duration of time between the Activity creation and when the user sees the UI for the Activity on their screen.

c20d14b151549937.png

Understanding the loading of a Fragment

Similar to the Activity the load time for a Fragment is defined as the time starting from when the Fragment gets attached to its host Activity all the way until the First Framefor the Fragment View is completely drawn on the screen.

5. Measure the load time of an Activity

Delays in the first frame can lead to a bad user experience, so it's important to understand how much initial load delay your users are experiencing. You can instrument a custom code trace to measure this load time:

  1. Start the custom code trace (named TestActivity-LoadTime ) in the Activity class as soon as the Activity object is created.

TestActivity.java

  public 
  
 class 
  
 TestActivity 
  
 extends 
  
 AppCompatActivity 
  
 { 
  
  
 // 
  
 TODO 
  
 ( 
 1 
 ): 
  
 Start 
  
 trace 
  
 recording 
  
 as 
  
 soon 
  
 as 
  
 the 
  
 Activity 
  
 object 
  
 is 
  
 created 
 . 
  
 private 
  
 final 
  
 Trace 
  
 viewLoadTrace 
  
 = 
  
 FirebasePerformance 
 . 
 startTrace 
 ( 
 "TestActivity-LoadTime" 
 ); 
  
 // 
  
 ... 
 } 
 
  1. Override the onCreate() callback, and get the View added by the setContentView() method.
  @Override 
  
 public 
  
 void 
  
 onCreate 
 ( 
 Bundle 
  
 savedInstanceState 
 ) 
  
 { 
  
  
 super 
 . 
 onCreate 
 ( 
 savedInstanceState 
 ); 
  
  
 // 
  
 Current 
  
 Activity 
 's main View (as defined in the layout xml file) is inflated after this 
 setContentView(R.layout.activity_test); 
 // ... 
 // TODO (2): Get the View added by Activity' 
 s 
  
 setContentView 
 () 
  
 method 
 . 
  
  
 View 
  
 mainView 
  
 = 
  
 findViewById 
 ( 
 android 
 . 
 R 
 . 
 id 
 . 
 content 
 ); 
  
  
 // 
  
 ... 
 } 
 
  1. We've included an implementation of FistDrawListener , which has two callbacks: onDrawingStart() and onDrawingFinish() (see the next section below for more details about FirstDrawListener and what can affect its performance) . Register the FirstDrawListener at the end of Activity's onCreate() callback. You should stop your viewLoadTrace in the onDrawingFinish() callback.

TestActivity.java

   
 // TODO (3): Register the callback to listen for first frame rendering (see 
  
 //  "OnFirstDrawCallback" in FirstDrawListener) and stop the trace when View drawing is 
  
 //  finished. 
  
 FirstDrawListener 
 . 
 registerFirstDrawListener 
 ( 
 mainView 
 , 
  
 new 
  
 FirstDrawListener 
 . 
 OnFirstDrawCallback 
 () 
  
 { 
  
  
 @ 
 Override 
  
  
 public 
  
 void 
  
 onDrawingStart 
 () 
  
 { 
  
  
 // In practice you can also record this event separately 
  
 } 
  
 @ 
 Override 
  
  
 public 
  
 void 
  
 onDrawingFinish 
 () 
  
 { 
  
 // This is when the Activity UI is completely drawn on the screen 
  
 viewLoadTrace 
 . 
 stop 
 (); 
  
  
 } 
  
  
 }); 
 
  1. Re-run the app. Then, filter the logcat with " Logging trace metric". Tap on the LOAD ACTIVITY button, and look for logs like below:
 I/FirebasePerformance: Logging trace metric: TestActivity-LoadTime (duration: XXXms) 

🎉 Congrats! You've successfully measured the loading time of an Activity and reported that data to Firebase Performance Monitoring. We'll view the recorded metric in the Firebase console later in this codelab.

Purpose of FirstDrawListener

In the section just above, we registered a FirstDrawListener . The purpose of FirstDrawListener is to measure when the first frame has begun and finished drawing.

It implements the ViewTreeObserver.OnDrawListener and overrides the onDraw() callback which is invoked when the View tree is about to be drawn. It then wraps the result to provide two utility callbacks onDrawingStart() and onDrawingFinish() .

The complete code for FirstDrawListener can be found in this codelab's source code .

6. Measure the load time of a Fragment

Measuring the load time of a Fragment is similar to how we measure it for an Activity but with some minor differences. Again, we'll instrument a custom code trace :

  1. Override the onAttach() callback and start recording your fragmentLoadTrace . We'll name this trace Test-Fragment-LoadTime .

As explained in an earlier step, the Fragment object can be created anytime, but it becomes active only when it's attached to its host Activity.

TestFragment.java

  public 
  
 class 
  
 TestFragment 
  
 extends 
  
 Fragment 
  
 { 
  
 // 
  
 TODO 
  
 ( 
 1 
 ): 
  
 Declare 
  
 the 
  
 Trace 
  
 variable 
 . 
  
 private 
  
 Trace 
  
 fragmentLoadTrace 
 ; 
  
 @ 
 Override 
  
 public 
  
 void 
  
 onAttach 
 ( 
 @ 
 NonNull 
  
 Context 
  
 context 
 ) 
  
 { 
  
 super 
 . 
 onAttach 
 ( 
 context 
 ); 
  
 // 
  
 TODO 
  
 ( 
 2 
 ): 
  
 Start 
  
 trace 
  
 recording 
  
 as 
  
 soon 
  
 as 
  
 the 
  
 Fragment 
  
 is 
  
 attached 
  
 to 
  
 its 
  
 host 
  
 Activity 
 . 
  
 fragmentLoadTrace 
  
 = 
  
 FirebasePerformance 
 . 
 startTrace 
 ( 
 "TestFragment-LoadTime" 
 ); 
  
 } 
 
  1. Register the FirstDrawListener in the onViewCreated() callback. Then, similar to the Activity example, stop the trace in the onDrawingFinish() .

TestFragment.java

  @Override 
 public 
  
 void 
  
 onViewCreated 
 ( 
 @NonNull 
  
 View 
  
 mainView 
 , 
  
 Bundle 
  
 savedInstanceState 
 ) 
  
 { 
  
 super 
 . 
 onViewCreated 
 ( 
 mainView 
 , 
  
 savedInstanceState 
 ); 
  
 // 
  
 ... 
  
 // 
  
 TODO 
  
 ( 
 3 
 ) 
 : 
  
 Register 
  
 the 
  
 callback 
  
 to 
  
 listen 
  
 for 
  
 first 
  
 frame 
  
 rendering 
  
 ( 
 see 
  
 // 
  
 "OnFirstDrawCallback" 
  
 in 
  
 FirstDrawListener 
 ) 
  
 and 
  
 stop 
  
 the 
  
 trace 
  
 when 
  
 view 
  
 drawing 
  
 is 
  
 // 
  
 finished 
 . 
  
 FirstDrawListener 
 . 
 registerFirstDrawListener 
 ( 
 mainView 
 , 
  
 new 
  
 FirstDrawListener 
 . 
 OnFirstDrawCallback 
 () 
  
 { 
  
 @Override 
  
 public 
  
 void 
  
 onDrawingStart 
 () 
  
 { 
  
 // 
  
 In 
  
 practice 
  
 you 
  
 can 
  
 also 
  
 record 
  
 this 
  
 event 
  
 separately 
  
 } 
  
 @Override 
  
 public 
  
 void 
  
 onDrawingFinish 
 () 
  
 { 
  
 // 
  
 This 
  
 is 
  
 when 
  
 the 
  
 Fragment 
  
 UI 
  
 is 
  
 completely 
  
 drawn 
  
 on 
  
 the 
  
 screen 
  
 fragmentLoadTrace 
 . 
 stop 
 (); 
  
 } 
  
 } 
 ); 
 
  1. Re-run the app. Then, filter the logcat with " Logging trace metric". Tap on the LOAD FRAGMENT button, and look for logs like below:
 I/FirebasePerformance: Logging trace metric: TestFragment-LoadTime (duration: XXXms) 

🎉 Congrats! You've successfully measured the loading time of a Fragment and reported that data to Firebase Performance Monitoring. We'll view the recorded metric in the Firebase console later in this codelab.

7. Understanding Screen Rendering and what is a Slow/Frozen frame

UI Rendering is the act of generating a frame from your app and displaying it on the screen. To ensure that a user's interaction with your app is smooth, your app should render frames in under 16ms to achieve 60 frames per second ( why 60fps? ). If your app suffers from slow UI rendering, then the system is forced to skip frames, and the user will perceive stuttering in your app. We call this jank.

Similarly, frozen frames are UI frames that take longer than 700ms to render. This delay is a problem because your app appears to be stuck and is unresponsive to user input for almost a full second while the frame is rendering.

8. Measure the Slow/Frozen frames of a Fragment

Firebase Performance Monitoring automatically captures slow/frozen frames for an Activity ( but only if it is Hardware Accelerated ). However, this feature is currently not available for Fragments. The slow/frozen frames of a Fragment is defined as the slow/frozen frames for the entire Activity between the onFragmentAttached() and onFragmentDetached() callbacks in the Fragment's lifecycle.

Taking motivation from the AppStateMonitor class ( which is a part of the Performance Monitoring SDK responsible for recording screen traces for Activity ), we implemented the ScreenTrace class ( which is part of this codelab source code repo ). ScreenTrace class can be hooked up to the Activity's FragmentManager 's lifecycle callback to capture slow/frozen frames. This class provides two public APIs:

  • recordScreenTrace() : Starts recording a screen trace
  • sendScreenTrace() : Stops the recording of a screen trace and attaches custom metrics to log Total, Slow, and Frozen frame counts

By attaching these custom metrics, screen traces for Fragments can be handled the same way as screen traces for an Activity and can be displayed along with other screen rendering traces in the Performance dashboard of the Firebase console.

Here's how to log screen traces for your Fragment:

  1. Initialize the ScreenTrace class in your Activity that hosts the Fragment.

MainActivity.java

  // 
  
 Declare 
  
 the 
  
 Fragment 
  
 tag 
 private 
  
 static 
  
 final 
  
 String 
  
 FRAGMENT_TAG 
  
 = 
  
 TestFragment 
 . 
 class 
 . 
 getSimpleName 
 (); 
 // 
  
 TODO 
  
 ( 
 1 
 ): 
  
 Declare 
  
 the 
  
 ScreenTrace 
  
 variable 
 . 
 private 
  
 ScreenTrace 
  
 screenTrace 
 ; 
 @ 
 Override 
 protected 
  
 void 
  
 onCreate 
 ( 
 Bundle 
  
 savedInstanceState 
 ) 
  
 { 
  
 super 
 . 
 onCreate 
 ( 
 savedInstanceState 
 ); 
  
 setContentView 
 ( 
 R 
 . 
 layout 
 . 
 activity_main 
 ); 
  
 // 
  
 TODO 
  
 ( 
 2 
 ): 
  
 Initialize 
  
 the 
  
 ScreenTrace 
  
 variable 
 . 
  
 screenTrace 
  
 = 
  
 new 
  
 ScreenTrace 
 ( 
 this 
 , 
  
 FRAGMENT_TAG 
 ); 
  
 // 
  
 ... 
 } 
 
  1. When you load your Fragment, register for FragmentLifecycleCallbacks and override the onFragmentAttached() and onFragmentDetached() callbacks. We have done this for you. You need to start recording screen traces in the onFragmentAttached() callback and stop recording in the onFragmentDetached() callback.

MainActivity.java

  private 
  
 final 
  
 FragmentManager 
 . 
 FragmentLifecycleCallbacks 
  
 fragmentLifecycleCallbacks 
  
 = 
  
 new 
  
 FragmentManager 
 . 
 FragmentLifecycleCallbacks 
 () 
  
 { 
  
 @Override 
  
 public 
  
 void 
  
 onFragmentAttached 
 ( 
 @NonNull 
  
 FragmentManager 
  
 fm 
 , 
  
 @NonNull 
  
 Fragment 
  
 f 
 , 
  
 @NonNull 
  
 Context 
  
 context 
 ) 
  
 { 
  
 super 
 . 
 onFragmentAttached 
 ( 
 fm 
 , 
  
 f 
 , 
  
 context 
 ); 
  
 // 
  
 TODO 
  
 ( 
 3 
 ) 
 : 
  
 Start 
  
 recording 
  
 the 
  
 screen 
  
 traces 
  
 as 
  
 soon 
  
 as 
  
 the 
  
 Fragment 
  
 is 
  
 // 
  
 attached 
  
 to 
  
 its 
  
 host 
  
 Activity 
 . 
  
 if 
  
 ( 
 FRAGMENT_TAG 
 . 
 equals 
 ( 
 f 
 . 
 getTag 
 ()) 
 && 
 screenTrace 
 . 
 isScreenTraceSupported 
 ()) 
  
 { 
  
 screenTrace 
 . 
 recordScreenTrace 
 (); 
  
 } 
  
 } 
  
 @Override 
  
 public 
  
 void 
  
 onFragmentDetached 
 ( 
 @NonNull 
  
 FragmentManager 
  
 fm 
 , 
  
 @NonNull 
  
 Fragment 
  
 f 
 ) 
  
 { 
  
 super 
 . 
 onFragmentDetached 
 ( 
 fm 
 , 
  
 f 
 ); 
  
 // 
  
 TODO 
  
 ( 
 4 
 ) 
 : 
  
 Stop 
  
 recording 
  
 the 
  
 screen 
  
 traces 
  
 as 
  
 soon 
  
 as 
  
 the 
  
 Fragment 
  
 is 
  
 // 
  
 detached 
  
 from 
  
 its 
  
 host 
  
 Activity 
 . 
  
 if 
  
 ( 
 FRAGMENT_TAG 
 . 
 equals 
 ( 
 f 
 . 
 getTag 
 ()) 
 && 
 screenTrace 
 . 
 isScreenTraceSupported 
 ()) 
  
 { 
  
 screenTrace 
 . 
 sendScreenTrace 
 (); 
  
 } 
  
 // 
  
 Unregister 
  
 Fragment 
  
 lifecycle 
  
 callbacks 
  
 after 
  
 the 
  
 Fragment 
  
 is 
  
 detached 
  
 fm 
 . 
 unregisterFragmentLifecycleCallbacks 
 ( 
 fragmentLifecycleCallbacks 
 ); 
  
 } 
  
 } 
 ; 
 
  1. Re-run the app. Then, tap on the LOAD FRAGMENT button. Wait for a few seconds, then click the back button on the bottom navigation bar.

Filter the logcat with " Logging trace metric", then look for logs like below:

 I/FirebasePerformance: Logging trace metric: _st_MainActivity-TestFragment (duration: XXXms) 

Filter the logcat with " FireperfViews", then look for logs like below:

 D/FireperfViews: sendScreenTrace MainActivity-TestFragment, name: _st_MainActivity-TestFragment, total_frames: XX, slow_frames: XX, frozen_frames: XX 

🎉 Congrats! You've successfully measured the Slow/Frozen frames for a Fragment and reported that data to Firebase Performance Monitoring. We'll view the recorded metrics in the Firebase console later in this codelab.

9. Check metrics in the Firebase console

  1. In the logcat, click the Firebase console URL to visit the details page for a trace.ceb9d5ba51bb6e89.jpeg

Alternatively, in the Firebase console , select the project that has your app. In the left panel, locate the Release & Monitor section, then click Performance.

  • In the main Dashboardtab, scroll down to the traces table, then click the Custom tracestab. In this table, you'll see the custom code traces we added earlier plus some out-of-the-box traces , such as _app_start trace.
  • Find your two custom code traces, TestActivity-LoadTime and TestFragment-LoadTime . Click on the Durationfor either one to view more details about the collected data.

a0d8455c5269a590.png

  1. The detail page for the custom code trace shows you information about the duration of the trace (i.e., the measured load time).

5e92a307b7410d8b.png

  1. You can also view the performance data for your custom screen trace.
  • Go back to the main Dashboard tab, scroll down to the traces table, then click the Screen rendering tab. In this table, you'll see the custom screen traces we added earlier plus any out-of-the-box screen traces , such as MainActivity trace.
  • Find your custom screen trace, MainActivity-TestFragment . Click the trace name to view the aggregated data of slow rendering and frozen frames.

ee7890c7e2c28740.png

10. Congratulations

Congratulations! You've successfully measured load time and screen rendering performance of an Activity and a Fragment by using Firebase Performance Monitoring!

What you have accomplished

What's next

Firebase Performance provides more ways of performance measurement of your app other than custom trace. It automatically measures app startup time, app-in-foreground, and app-in-background performance data . It's time for you to check these metrics in the Firebase Console .

Also, Firebase Performance offers automatic HTTP/S network request monitoring . With that you can easily instrument network requests without writing a single line of code. Can you try sending some network requests from your app and find the metrics in the Firebase console ?

Bonus

Now that you know how to measure the load time and screen rendering performance of your Activity/Fragment by using custom code traces, can you explore our open sourced code base to see if you can capture those metrics out of the box for any Activity/Fragment that is a part of the app? Feel free to send the PR if you wish :-)

11. Bonus Learning

Understanding what's happening during the loading of an Activity will help you better understand the performance characteristics of your app. In an earlier step, we described at a high level what happens during the loading of an Activity, but the following diagram describes each phase in much higher detail.

cd61c1495fad7961.png

Create a Mobile Website
View Site in Mobile | Classic
Share by: