Cold start optimization
Skill almasumdev/awesome-mobile-performance-agent-skills/.github/skills/startup/cold-start-optimization
Agent skills for profiling and optimizing mobile app performance (startup, memory, frame-rate, network).
npx -y skills add almasumdev/awesome-mobile-performance-agent-skills --skill cold-start-optimizationAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Reduce cold start time (TTID and TTFD) on iOS and Android using baseline profiles, main-thread hygiene, and selective pre-warming. Use when launch latency exceeds the budget in Agent.md.
SKILL.md
5.0 KB, as published. Nobody here has run it
Cold Start Optimization
Instructions
Optimize cold start — the scenario where the OS creates a new process and your app draws its first meaningful frame. Cold start is what app-store reviewers and lab devices measure, so it is the number that matters for reviews and rankings.
1. Define the Target Metrics
Two numbers matter:
- TTID (Time To Initial Display) — first frame drawn. On Android this is
Displayedin logcat. On iOS it is the momentUIWindowcommits its first frame. - TTFD (Time To Full Display) — first useful frame with real data. On Android call
Activity.reportFullyDrawn(). On iOS emit anos_signpostevent namedTTFD.
Targets (p50 device, release build):
| Metric | iOS budget | Android budget |
|---|---|---|
| TTID | 1.2 s | 1.5 s |
| TTFD | 2.0 s | 2.5 s |
2. Baseline First
Before changing code, capture a baseline.
Android (Macrobenchmark):
@RunWith(AndroidJUnit4::class)
class StartupBenchmark {
@get:Rule val rule = MacrobenchmarkRule()
@Test fun coldStartup() = rule.measureRepeated(
packageName = "com.example.app",
metrics = listOf(StartupTimingMetric()),
iterations = 10,
startupMode = StartupMode.COLD,
) { pressHome(); startActivityAndWait() }
}
iOS (XCTest):
func testLaunchPerformance() {
measure(metrics: [XCTApplicationLaunchMetric()]) {
XCUIApplication().launch()
}
}
3. Make the Main Thread Boring
Every millisecond before the first frame is paid on the main thread. Audit and remove:
- Disk IO in
Application.onCreate/UIApplicationDelegate.application(_:didFinishLaunchingWithOptions:). - Synchronous network calls, feature-flag fetches, remote-config SDK init.
- Eager DI container construction (Hilt
@EntryPoint, Swinject assembler,get_itregistrations) for features not on the start screen. - JSON parsing of cached payloads. Defer to
Dispatchers.IO/ a backgroundDispatchQueue. - Heavy reflection or annotation scanning (Jackson, Gson with
@SerializedNameat startup).
Android pattern — use Initializer from androidx.startup and disable default init for expensive libs:
class AnalyticsInitializer : Initializer<Analytics> {
override fun create(ctx: Context): Analytics = Analytics.init(ctx)
override fun dependencies(): List<Class<out Initializer<*>>> = emptyList()
}
iOS pattern — replace +[load] with +initialize or lazy static let. Never do work in +[load].
enum FeatureFlags {
static let shared = FeatureFlagsClient() // lazily created on first access
}
4. Ship a Baseline Profile (Android)
Baseline Profiles AOT-compile the hot startup path and typically reduce cold start by 15–30%.
@RunWith(AndroidJUnit4::class)
class BaselineProfileGenerator {
@get:Rule val rule = BaselineProfileRule()
@Test fun generate() = rule.collect(packageName = "com.example.app") {
pressHome(); startActivityAndWait()
device.findObject(By.res("feed")).scroll(Direction.DOWN, 0.8f)
}
}
Commit the produced baseline-prof.txt into src/main/ and add androidx.profileinstaller.
5. Pre-warm Only What the First Frame Needs
- iOS: prebuild the root
UIView/ SwiftUIViewtree off the critical path usingTask.detachedfor side data only — never for the first frame's view tree. - Android: in
Application.onCreate, post non-critical work withHandler(Looper.getMainLooper()).post {}so it runs after first frame. - Flutter: use
SchedulerBinding.instance.addPostFrameCallbackto defer work until after the first frame; mark shell route withHeroMode(enabled: false)during launch. - React Native: enable Hermes; move bridge-heavy native modules to lazy registration via
TurboModuleon the New Architecture.
6. Splash Screen, Not Blank Window
Use the OS-provided splash APIs — SplashScreen on Android 12+, LaunchScreen.storyboard / .xcassets on iOS. A themed splash hides the process-creation window and improves perceived TTID without adding real time.
7. Measure Again, Record in CI
Add the benchmark to CI. Fail the build on > 5% regression vs a committed baseline JSON. See startup-profiling skill.
Checklist
- TTID and TTFD budgets documented.
- Baseline numbers captured on a p50 device in release/profile mode.
- No disk IO, network, or JSON parsing on the main thread before first frame.
- Expensive SDK initializers moved off the startup path (AndroidX Startup, lazy singletons).
- Android: Baseline Profile generated and installed via
androidx.profileinstaller. - iOS: no work in
+[load];Task.detachedused for side effects only. - Splash theme / launch screen configured to avoid white flash.
- CI benchmark fails on > 5% regression.