Android Performance

Android Performance Optimization: Overdraw - Theory

Word count: 1.4kReading time: 8 min
2014/10/20
loading

It’s been a while since my last update. After joining a new company, things have been busy, but I’ve been spending a lot of time researching Android performance. I’ve realized there’s so much I still don’t know, so I’m starting from the application level and working my way down. This series will document my learnings on Android performance optimization.

First, we’ll discuss GPU Overdraw, which is often the most direct point of contact for developers. This topic is split into two parts: Part 1 covers the theory and optimization suggestions, and Part 2 will walk through a practical optimization example.

What is Overdraw?

GPU Overdraw refers to the system drawing more than one layer on a single pixel during a frame. For example, if a TextView has a background color, the pixels displaying the text are drawn twice: once for the background and once for the characters. Overdraw inevitably impacts performance because memory bandwidth is finite. When overdraw exceeds the available bandwidth, the frame rate drops. Bandwidth limits vary significantly across different devices.

Causes of Overdraw

  1. Too many overlapping Views.
  2. Complex, deeply nested view hierarchies.
  3. Long inflation times due to complex XML.

Impact of Poor XML Layouts

  1. Inflation Overhead: Layout files are XML. Inflation involves parsing XML, creating objects, and linking them. More tags, attributes, and deeper trees increase the time spent in parsing, recursion, and function calls.
  2. Lifecycle Bottlenecks: Inflation is just the beginning. After requestLayout, the system must execute measure, layout, and draw. The time for each step is dictated by layout quality. Poorly designed layouts increase the cost of every frame, leading to noticeable delays in rendering.

Basic Tools for Detecting Overdraw

Android provides three primary tools: Hierarchy Viewer, Tracer for OpenGL, and Show GPU Overdraw. The first two are found in the SDK monitor tools, while the last is a built-in Developer Option.

  1. Testing: Enable “Debug GPU Overdraw” in Developer Options. (On some devices like Meizu, look under Accessibility -> Developer Tools -> Hardware Accelerated Rendering -> Debug GPU Overdraw -> Show Overdraw Areas. Note: Meizu phones require enabling developer mode first by dialing *#*#6961#*#* in the phone app). This feature is available only on Android 4.2 and above.
  2. Color Coding: Overdraw is visualized from best to worst: Blue -> Green -> Light Red -> Red.
    • Blue: 1x overdraw (optimal).
    • Green: 2x overdraw.
    • Light Red: 3x overdraw.
    • Red: 4x+ overdraw (critical issue).
  3. Acceptance Criteria:
    • Aim to keep overdraw at 2x or lower.
    • Avoid 4x overdraw entirely.
    • Avoid 3x overdraw (Light Red) areas larger than 1/4 of the screen.

Optimization Tools

1. Lint

  • Eclipse: Click to run, suggestions appear in the bottom window.
  • Android Studio: Built-in Lint tool highlights suboptimal code or layouts in yellow.
  • Lint provides excellent suggestions for both UI and code risks. A good rule of thumb is to clear as many Lint warnings as possible.
  • It can also be run via the command line. For details, refer to: tools.android.com

Lint

2. Common Lint Suggestions (from official documentation)

  • Use compound drawables: A LinearLayout containing an ImageView and a TextView can often be replaced by a single TextView with a drawableStart (compound drawable).
  • Merge root frame: If a FrameLayout is the root and provides no background/padding, replace it with a <merge> tag to flatten the hierarchy.
  • Remove useless leaves: A layout with no children or background can often be removed (since it’s invisible) for a flatter and more efficient layout hierarchy.
  • Remove useless parents: A layout with children that has no siblings, is not a ScrollView or root layout, and has no background can be removed, moving its children directly into the parent for a flatter hierarchy.
  • Flatten Deep Layouts: Avoid deep nesting. Use RelativeLayout or GridLayout to improve performance. The default maximum depth warning threshold is configurable.

3. Hierarchy Viewer (HV)

This tool is part of ADT (or monitor - newer SDK versions recommend using monitor instead of standalone HV). It’s invaluable for analyzing view hierarchies and identifying layout issues. By default, HV works only on non-encrypted devices (engineering devices, tablets, or emulators). To use it on any phone, you need to add ViewServer to your app (an open-source library). Connect your device, open Hierarchy Viewer (located in tools/ directory, run hierarchyviewer command), select the target process, and click “Load View Hierarchy” to display the current interface’s layout tree. Each node shows three traffic lights representing the performance of Measure, Layout, and Draw operations.

Layout Optimization Principles

By following these best practices, you can create high-performance, reusable UIs:

  • Prefer RelativeLayout and LinearLayout over AbsoluteLayout.
    • For layouts with similar hierarchy depth, LinearLayout is slightly more efficient than RelativeLayout.
    • For complex layouts, use RelativeLayout to avoid the nesting that LinearLayout would require.
  • Extract reusable components using the <include> tag.
  • Use <ViewStub> for layouts not needed at startup.
  • Dynamic inflation performs better than setVisibility(). However, ViewStub is the best choice.
  • Use the <merge> tag to reduce hierarchy depth.
  • Remove redundant backgrounds: Use Hierarchy Viewer to export layers as PSD files and inspect in Photoshop (see this video tutorial).
    • For multi-layered layouts, only the top layer should have a solid background color.
    • For selectors used as backgrounds (e.g., in ListView items), set the “normal” state to @android:color/transparent.
  • Avoid layout_weight in nested LinearLayouts, as it requires children to be measured twice. This is especially critical in ListView and GridView where items are repeatedly created.
  • Keep layouts broad and shallow instead of narrow and deep (visible in Hierarchy Viewer’s Tree view).

Source Code References

For those interested in the underlying implementation:

Overdraw rendering logic is located at: /frameworks/base/libs/hwui/OpenGLRenderer.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
void OpenGLRenderer::renderOverdraw() {
if (mCaches.debugOverdraw && getTargetFbo() == 0) {
const Rect* clip = &mTilingClip;

mCaches.enableScissor();
mCaches.setScissor(clip->left, mFirstSnapshot->height - clip->bottom,
clip->right - clip->left, clip->bottom - clip->top);

// 1x overdraw
mCaches.stencil.enableDebugTest(2);
drawColor(mCaches.getOverdrawColor(1), SkXfermode::kSrcOver_Mode);

// 2x overdraw
mCaches.stencil.enableDebugTest(3);
drawColor(mCaches.getOverdrawColor(2), SkXfermode::kSrcOver_Mode);

// 3x overdraw
mCaches.stencil.enableDebugTest(4);
drawColor(mCaches.getOverdrawColor(3), SkXfermode::kSrcOver_Mode);

// 4x overdraw and higher
mCaches.stencil.enableDebugTest(4, true);
drawColor(mCaches.getOverdrawColor(4), SkXfermode::kSrcOver_Mode);

mCaches.stencil.disable();
}
}

void OpenGLRenderer::countOverdraw() {
size_t count = mWidth * mHeight;
uint32_t* buffer = new uint32_t[count];
glReadPixels(0, 0, mWidth, mHeight, GL_RGBA, GL_UNSIGNED_BYTE, &buffer[0]);

size_t total = 0;
for (size_t i = 0; i < count; i++) {
total += buffer[i] & 0xff;
}

mOverdraw = total / float(count);

delete[] buffer;
}

For QA teams, there’s also an OverDraw numerical metric. The source code for this is located in Framework/base/core/java/android/view/HardwareRender.java (note: this display was removed in Android 5.0):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
private void debugOverdraw(View.AttachInfo attachInfo, Rect dirty,
HardwareCanvas canvas, DisplayList displayList) {

if (mDebugOverdraw == OVERDRAW_TYPE_COUNT) {
if (mDebugOverdrawLayer == null) {
mDebugOverdrawLayer = createHardwareLayer(mWidth, mHeight, true);
} else if (mDebugOverdrawLayer.getWidth() != mWidth ||
mDebugOverdrawLayer.getHeight() != mHeight) {
mDebugOverdrawLayer.resize(mWidth, mHeight);
}

if (!mDebugOverdrawLayer.isValid()) {
mDebugOverdraw = -1;
return;
}

HardwareCanvas layerCanvas = mDebugOverdrawLayer.start(canvas, dirty);
countOverdraw(layerCanvas);
final int restoreCount = layerCanvas.save();
layerCanvas.drawDisplayList(displayList, null, DisplayList.FLAG_CLIP_CHILDREN);
layerCanvas.restoreToCount(restoreCount);
mDebugOverdrawLayer.end(canvas);

float overdraw = getOverdraw(layerCanvas);
DisplayMetrics metrics = attachInfo.mRootView.getResources().getDisplayMetrics();

drawOverdrawCounter(canvas, overdraw, metrics.density);
}
}

private void drawOverdrawCounter(HardwareCanvas canvas, float overdraw, float density) {
final String text = String.format("%.2fx", overdraw);
final Paint paint = setupPaint(density);
// HSBtoColor will clamp the values in the 0..1 range
paint.setColor(Color.HSBtoColor(0.28f - 0.28f * overdraw / 3.5f, 0.8f, 1.0f));

canvas.drawText(text, density * 4.0f, mHeight - paint.getFontMetrics().bottom, paint);
}

Reference Articles

  1. Optimization Process
  2. Decompiling and Adding GPU Display
  3. Optimizing Layouts
  4. Reusing Layouts
  5. Loading Layouts On-Demand
  6. Smooth Scrolling
  7. Hierarchy Viewer Documentation
  8. Lint Tips

About Me && Blog

Here’s my personal introduction and related links. I look forward to exchanging ideas with fellow developers - “When three walk together, there must be one who can be my teacher!”

  1. About the Author : Includes my WeChat and WeChat group links.
  2. Blog Content Navigation : A navigation guide to my blog content.
  3. Personally Curated Excellent Blog Articles - Must-Know Android Performance Optimization : Welcome to recommend articles (contact me via WeChat).
  4. Android Performance Optimization Knowledge Planet : Welcome to join, thank you for your support!

One person can walk faster, but a group can walk farther.

WeChat QR Code

CATALOG
  1. 1. What is Overdraw?
  2. 2. Causes of Overdraw
  3. 3. Impact of Poor XML Layouts
  4. 4. Basic Tools for Detecting Overdraw
  5. 5. Optimization Tools
    1. 5.1. 1. Lint
    2. 5.2. 2. Common Lint Suggestions (from official documentation)
    3. 5.3. 3. Hierarchy Viewer (HV)
  6. 6. Layout Optimization Principles
  7. 7. Source Code References
  8. 8. Reference Articles
  • About Me && Blog