Tune lazy-list performance
Required dependencies
This page is standalone. Collection policies are part of the base UI contract; no optional performance artifact is required:
repositories { mavenCentral() }
dependencies {
implementation("com.viewcompose:viewcompose-material3-android:0.1.0-alpha01")
implementation("androidx.activity:activity:1.12.4")
implementation("com.google.android.material:material:1.13.0")
}
Add measured collection hints
Create LazyListPerformanceTutorialActivity.kt:
package com.viewcompose.samples.tutorials
import android.os.Bundle
import androidx.activity.ComponentActivity
import com.viewcompose.material3.android.setMaterial3UiContent
import com.viewcompose.ui.modifier.Modifier
import com.viewcompose.ui.modifier.fillMaxSize
import com.viewcompose.ui.modifier.fillMaxWidth
import com.viewcompose.ui.modifier.padding
import com.viewcompose.ui.node.policy.CollectionReusePolicy
import com.viewcompose.ui.node.policy.LazyLayoutPrefetchPolicy
import com.viewcompose.ui.unit.dp
import com.viewcompose.ui.foundation.LazyColumn
import com.viewcompose.ui.foundation.Text
class LazyListPerformanceTutorialActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setMaterial3UiContent {
val rows = List(500) { index -> "Row #${index + 1}" }
LazyColumn(
items = rows,
key = { row -> row },
contentType = { "text-row" },
contentRevision = { row -> row },
prefetchPolicy = LazyLayoutPrefetchPolicy(
nestedInitialPrefetchItemCount = 4,
itemViewCacheSize = 4,
),
reusePolicy = CollectionReusePolicy(
sharePool = true,
mountedTreeCacheSize = 2,
),
modifier = Modifier.fillMaxSize().padding(16.dp),
) { row ->
Text(row, modifier = Modifier.fillMaxWidth().padding(8.dp))
}
}
}
}
Prefetch and cache sizes are bounded renderer policies; they never define business state. Shared
pools retain only empty holder shells. The mounted-tree cache retains reset physical trees by
contentType and releases them deterministically on eviction. contentRevision does define item
semantics: when a captured non-State value changes, its revision must change too. Measure before
increasing either cache because larger values consume more memory.
If a stable parent recomposes often and selector scans appear in the profile, introduce a remembered
LazyItemsSnapshot at the application's immutable data boundary. It is not a general replacement
for an ordinary list: creating a new snapshot on every composition still performs the scan, while
retaining one after ordinary data or captures change is incorrect.
Verify the result
Profile scrolling with the default values and with the explicit policy on the same release build and device. Compile the sample with:
./gradlew :samples:tutorials:assembleDebug
Use the Performance guide for benchmark conditions and regression budgets.