This codelab is part of the Advanced Android Development training course, developed by the Google Developers Training team. You will get the most value out of this course if you work through the codelabs in sequence.

For complete details about the course, see the Advanced Android Development overview.

Introduction

Every app is different and could have different performance issues. While there are best practices, you need to analyze the unique configuration of your app to find and fix performance issues.

To discover what causes your app's specific performance problems, use tools to collect data about your app's execution behavior. Understand and analyze what you see, then improve your code. Android Studio and your device provide profiling tools to record and visualize the rendering, compute, memory, and battery performance of your app.

In this practical, you use the Profile GPU Rendering tool on your device to visualize how long it takes an app to draw frames to the screen.

Your app must consistently draw the screen fast enough for your users to see smooth, fluid motions, transitions, and responses. To this end, typically, modern devices strive to display 60 frames per second (a frame rate of 60 FPS), making 16 milliseconds available to each frame. Therefore, your app has to do its work in 16 milliseconds or less for every screen refresh.

What you should already know

You should be able to download an app from GitHub, open it with Android Studio, and run it.

It helps to be familiar with the following concepts and terminology:

As a refresher, watch the Invalidation, Layouts, and Performance video for an overview of the Android rendering pipeline. Read the Rendering and layout concept chapter as well as How Android Draws Views.

What you'll learn

You will learn how to:

What you'll do

The following screenshot shows one way of implementing the LargeImages app.

Putting pixels on the screen involves four primary pieces of hardware:

Each of these pieces of hardware has constraints. Stressing or exceeding those constraints can cause your app to be slow, have bad display performance, or exhaust the battery.

The Profile GPU Rendering tool gives you a quick visual representation of how much time it takes to render the frames of a UI window relative to the 16-ms-per-frame benchmark. The tool shows a colored bar for each frame. Each bar has colored segments indicating the relative time that each stage of the rendering pipeline takes, as shown in the screenshot below.

What it's good for:

1.1 Disable Instant Run

While profiling an app, disable Instant Run in Android Studio. There is a small performance impact when using Instant Run. This performance impact could interfere with information provided by performance profiling tools. Additionally, the stub methods generated while using Instant Run can complicate stack traces. See About Instant Run for details.

In Android Studio, turn off Instant Run for the physical and virtual devices you want to profile.

  1. Open the Settings or Preferences dialog: On Windows or Linux, select File > Settings from the menu bar. On Mac OSX, select Android Studio > Preferences from the menu bar.
  2. Navigate to Build, Execution, Deployment > Instant Run.
  3. Deselect the Restart activity on code changes checkbox.

1.2 Install and run the RecyclerView app

For this tool walkthrough, use the RecyclerView app which shows a list of words displayed in a RecyclerView.

  1. Download the RecyclerView app.
  2. Open the app in Android Studio and run it. You may need to uninstall previous versions of the app.
  3. Interact with the app. Does the app scroll smoothly?
  4. In MainActivity.java, in onCreate, change the initial number of words created from 20 to 200. . Does this change how you perceive the performance of the app?

RecyclerView is an efficient way of displaying information, and on most devices and recent versions of Android, the app performs smoothly. What users perceive and how they experience the app are your ultimate benchmarks for performance.

1.3 Turn on the Profile GPU Rendering tool

Ensure that developer options is turned on, and allow USB Debugging if prompted.

Go to Settings > Developer options and follow the steps as illustrated in the screenshot below.

  1. Scroll down to the Monitoring section.
  2. Select Profile GPU rendering.
  3. Choose On screen as bars in the dialog box. Immediately, you see colored bars on your screen. The bars vary depending on your device and version of Android, so they may not look exactly as illustrated.

  1. Switch to the RecyclerView app and interact with it.

Note the following:

This is not an absolute requirement. For example, when you first load an image, the bar may go above the green line, but users may not notice a problem, because they may expect to wait a moment for an image to load. Having some tall bars does not mean your app has a performance problem. However, if your app does have a performance problem, tall bars can give you a hint as to where to start looking.

The image below shows the bars and color legend for a device that is running Android 6.0 or higher. The bars for older versions use different coloring. See the Profile GPU Rendering Walkthrough for the bar legend for older versions.

The following table shows the component bars in Android 6.0 and higher.

Color

Rendering stage

Description

Swap buffers

Represents the time the CPU is waiting for the GPU to finish its work. If this part of the bar is tall, the app is doing too much work on the GPU.

Command issue

Represents the time spent by Android's 2-D renderer as it issues commands to OpenGL to draw and redraw display lists. The height of this part of the bar is directly proportional to the sum of the time it takes for all the display lists to execute—more display lists equals a taller red segment of the bar.

Sync and upload

Represents the time it take to upload bitmap information to the GPU. If this part of the bar is tall, the app is taking considerable time loading large amounts of graphics.

Draw

Represents the time used to create and update the view's display lists. If this part of the bar is tall, there may be a lot of custom view drawing, or a lot of work in onDraw methods.

Measure and layout

Represents the amount of time spent on onLayout and onMeasure callbacks in the view hierarchy. A large segment indicates that the view hierarchy is taking a long time to process.

Animation

Represents the time spent evaluating animators. If this part of the bar is tall, your app could be using a custom animator that's not performing well, or unintended work is happening as a result of properties being updated.

Input handling

Represents the time that the app spent executing code inside of an input event callback. If this part of the bar is tall, the app is spending too much time processing user input. Consider offloading such processing to a different thread.

Misc. time / Vsync delay

Represents the time that the app spends executing operations between consecutive frames. It might indicate too much processing happening in the UI thread that could be offloaded to a different thread. (Vsync is a display option found in some 3-D apps.)

For example, if the green Input handling portion of the bar is tall, your app spends a lot of time handling input events, executing code called as a result of input event callbacks. To improve this, consider when and how your app requests user input, and whether you can handle it more efficiently.

Note that RecyclerView scrolling can show up in the Input handling portion of the bar. RecyclerView scrolls immediately when it processes the touch event. For this reason, processing the touch event needs to be as fast as possible.

If you spend time using the Profile GPU Rendering tool on your app, it will help you identify which parts of the UI interaction are slower than expected, and then you can take action to improve the speed of your app's UI.

2.1 Create an app with image backgrounds

Build a LargeImages app that has performance problems. (You re-use this app in a later practical.)

One way to implement the LargeImages app is to show simple instructions in a TextView and the large background image that is provided with the solution code.

Do the following:

  1. Create an app named LargeImages using the Empty Template.
  2. Choose 5.1, Lollipop (API 22) as the minimum SDK. (Lossless and transparent WebP images that you use later in this app are supported in Android 4.3, API level 18 and higher, and API 22 is recommended.)
  3. Put at least one small and one large image into the drawables resource folder.
  1. Add an Activity.
  2. Create a layout with a LinearLayout with a TextView. Set the height and width of the TextView to match_parent.
  3. Set the background (android:background) on the TextView to the small image.
  4. Add this text to this to the TextView: "Click to swap image."
  5. Add a click handler to the TextView that changes the background when the screen is tapped. Use the following code snippet as a template. The code uses a numeric toggle so that you can experiment with any number of images.
private int toggle = 0;
...
public void changeImage(View view){ 
     if (toggle == 0) {
       view.setBackgroundResource(R.drawable.dinosaur_medium);
       toggle = 1;
   } else {
       view.setBackgroundResource(R.drawable.ankylo);
       toggle = 0;
   }
}
  1. Run the app with Profile GPU Rendering turned on and tap to swap pictures. When you load the small image, you should see relatively short bars. When you load the large image, there should be relatively tall bars, similar to the ones shown below.

Profile GPU Rendering bars for the large image app are shown in the previous screenshots. The detail on the left shows the faint short bars for the small image (1) staying below the green line (2). The screenshot on the right show the bars as they appear on your device emphasizing the bars that cross the green line (3,4).

  1. The narrow, faint bar for the small image is below the green line, so there is no performance issue.
  2. The green horizontal line indicates the 16 millisecond rendering time. Ideally, most bars should be close to or below this line.
  3. After the small image is replaced by the large image, the red bar segment is huge. This Command issue segment represents the time spent by Android's 2D renderer issuing commands to draw and redraw display lists.
  4. The large image also has a huge orange segment. This Swap buffers segment represents the time the CPU is waiting for the GPU to finish its work.

The GPU works in parallel with the CPU. The Android system issues draw commands to the GPU, and then moves on to its next task. The GPU reads those draw commands from a queue. When the CPU issues commands faster than the GPU consumes them, the queue between the processors becomes full, and the CPU blocks. The CPU waits until there is space in the queue to place the next command.

To mitigate this problem, reduce the complexity of work occurring on the GPU, similar to what you would do for the "Command issue" phase.

See Analyzing with Profile GPU Rendering for details on each stage.

To fix the app, you would use a different or smaller image. Finding problems can be as straightforward as this, or it can take additional analysis to pinpoint what is happening in your app's code.

Troubleshooting

2.2 Simulate long input processing

To demonstrate how doing too much work on the UI thread slows down your app, slow down drawing by putting the app to sleep:

  1. Add code to the click handler of the LargeImages app to let your app sleep for two screen refreshes before switching the background to the smaller image. This means that instead of refreshing the screen every 16 ms, your app now refreshes every 48 ms with new content. This will be reflected in the bars displayed by the Profile GPU Rendering tool.
public void changeImage(View view){
   if (toggle == 0) {
       view.setBackgroundResource(R.drawable.dinosaur_large);
       toggle = 1;
   } else {
       try {
           Thread.sleep(32); // two refreshes
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
       view.setBackgroundResource(R.drawable.ankylo);
       toggle = 0;
   }
}
  1. Run your app. The previously short bars for the small image should now be much taller. Wait a while before clicking the TextView to get the next image; otherwise, the Android system may display an ANR.

If your app is only swapping backgrounds, the impact from this change may not be significant for the user. However, if you are running animated content, skipping two out of three frames will result in a stuttering and jagged animation.

While Profile GPU Rendering cannot tell you exactly what to fix in your code, it can hint at where you should start looking. And let's admit it, the on-screen bars are pretty cool!

Android Studio project: LargeImages.

The related concept documentation is in 4.0 Performance and 4.1 Rendering and layout.

Android developer documentation:

This section lists possible homework assignments for students who are working through this codelab as part of a course led by an instructor. It's up to the instructor to do the following:

Instructors can use these suggestions as little or as much as they want, and should feel free to assign any other homework they feel is appropriate.

If you're working through this codelab on your own, feel free to use these homework assignments to test your knowledge.

Build and run an app

Build an app that shows a scrollable list of items for sale, with thumbnail images. When the user taps an image, the app displays a details-view popup with a larger version of the image. You will use this app to complete other performance homework assignments.

Answer these questions

Question 1

Select all of the following that are good basic performance tests you can perform.

Question 2

Select all that are good ways to approach performance problems.

Question 3

Select all of the following that are performance tools available on your mobile device.

Submit your app for grading

App should show a list or grid of items with thumbnail images. When you tap an item, a larger image of the item is displayed.

Guidance for graders

Check that the app has the following features:

To see all the codelabs in the Advanced Android Development training course, visit the Advanced Android Development codelabs landing page.