androidengineers.Book a session

Performance Optimization

Layout Optimizations & Overdraw

article25 minMedium

Layout inflation and rendering are two distinct stages where slowness manifests as jank. Optimizing both requires understanding the rendering pipeline and using the right layout tools.

Overdraw

Overdraw occurs when a pixel is drawn more than once per frame. It's wasted GPU work. Visualize it in Developer Options → Debug GPU Overdraw:

ColorOverdrawState
WhiteIdeal
BlueAcceptable
GreenAcceptable
PinkInvestigate
Red4×+Fix required

Common causes:

  • Background set on both Window and layout root
  • Nested Views each with their own background
  • Card-inside-card layouts with backgrounds

Fix:

<!-- ❌ Window background + root layout background = overdraw -->
<LinearLayout
    android:background="@color/white"  <!-- redundant if window is white -->
    ...>

<!-- ✅ Remove redundant background -->
<!-- In theme: <item name="android:windowBackground">@color/white</item> -->
<!-- In layout: no background on root if it matches window background -->

View Hierarchy Flattening

Deep view hierarchies are expensive to measure and lay out. Use ConstraintLayout to express complex layouts in a flat hierarchy:

<!-- ❌ Deep nesting: 5 levels, 4 layouts -->
<LinearLayout> <!-- level 1 -->
    <LinearLayout> <!-- level 2 -->
        <LinearLayout> <!-- level 3 -->
            <FrameLayout> <!-- level 4 -->
                <TextView /> <!-- level 5 -->
<!-- ✅ Flat: 1 level, constraints express the same layout -->
<ConstraintLayout>
    <TextView
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        ... />
    <ImageView
        app:layout_constraintTop_toBottomOf="@id/textView"
        ... />
</ConstraintLayout>

Lint check: Hierarchy Viewer (deprecated) → Layout Inspector shows the full tree. Use adb shell dumpsys gfxinfo com.example.myapp for measure/layout counts.

ViewStub: Lazy Layout Inflation

For views that aren't always shown (error states, empty states, settings panels):

<ViewStub
    android:id="@+id/errorStub"
    android:layout="@layout/error_state"
    android:inflatedId="@+id/errorView"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />
// Inflate only when needed
binding.errorStub.inflate()  // replaces ViewStub with the actual layout

// Or use visibility-based approach with binding:
binding.errorStub.setOnInflateListener { _, inflated ->
    val errorView = ErrorStateBinding.bind(inflated)
    errorView.retryButton.setOnClickListener { retry() }
}

Include & Merge

Use <include> to reuse layouts. Use <merge> as the root of included layouts to avoid adding an extra ViewGroup:

<!-- loading_spinner.xml — use <merge> to avoid extra FrameLayout -->
<merge xmlns:android="http://schemas.android.com/apk/res/android">
    <ProgressBar ... />
    <TextView android:text="Loading..." ... />
</merge>

<!-- In parent layout: -->
<include layout="@layout/loading_spinner" />

Avoid wrap_content on Large Lists

RecyclerView with wrap_content height triggers two measure passes — expensive for long lists. Use match_parent or a fixed height:

<!-- ❌ Two measure passes -->
<androidx.recyclerview.widget.RecyclerView
    android:layout_height="wrap_content" />

<!-- ✅ Single measure pass -->
<androidx.recyclerview.widget.RecyclerView
    android:layout_height="0dp"
    app:layout_constraintTop_toBottomOf="@id/header"
    app:layout_constraintBottom_toBottomOf="parent" />

Layout Inspector: Diagnosing Issues

Android Studio → Layout Inspector while app is running:

  1. See real-time 3D view of view hierarchy
  2. Highlight over-deep nesting
  3. Click any view to see its attributes and measured size
  4. Rotate the 3D view to see which layers are behind others (overdraw hint)

Key Takeaways

OptimizationImpact
Remove redundant backgroundsReduces overdraw from 3× to 1×
ConstraintLayout flat hierarchyReduces measure passes; improves scroll performance
ViewStub for conditional viewsAvoids inflating unused views at startup
<merge> in <include>Removes one ViewGroup per reusable layout
Fixed RecyclerView heightPrevents double measure pass

YOUR LEARNING JOURNEY

0 of 177 available lessons completed

Progress saved in this browser. No account needed.
Layout Optimizations & Overdraw | Android System Design | Android Engineers