Mobile Performance Optimization
Optimize mobile app performance for smooth 60fps rendering, fast startup, efficient memory usage, and minimal battery drain. Covers profiling tools, rendering optimization, memory management, network efficiency, and the metrics that matter.
Mobile performance is unforgiving. Users perceive jank at 16ms frame drops. App startup over 3 seconds triggers abandonment. Battery drain makes users uninstall. Unlike web, you cannot just add more servers — the hardware is fixed, shared with other apps, and constrained by battery.
Key Metrics
| Metric | Good | Acceptable | Poor |
|---|---|---|---|
| Cold start time | < 1s | 1-2s | > 3s |
| Frame rate | 60 fps (16ms) | 45 fps | < 30 fps |
| Memory usage | < 100 MB | 100-200 MB | > 300 MB |
| App size | < 30 MB | 30-100 MB | > 200 MB |
| Battery impact | Background: < 1%/hr | 1-3%/hr | > 5%/hr |
Startup Optimization
Cold Start Phases
Process creation → Application init → First frame
Optimization targets:
1. Reduce library initialization (lazy load)
2. Defer non-critical work (analytics, crash reporting)
3. Show UI immediately, load data asynchronously
4. Precompute or cache expensive calculations
iOS Startup
// Defer non-essential initialization
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Essential only: Core Data, Auth check
setupCoreData()
checkAuth()
// Defer everything else
DispatchQueue.main.async {
self.setupAnalytics()
self.setupCrashReporting()
self.prefetchData()
}
return true
}
Android Startup
class App : Application() {
override fun onCreate() {
super.onCreate()
// Use App Startup library for lazy initialization
// Essential: Timber logging, Room database
Timber.plant(DebugTree())
// Defer to background thread
lifecycleScope.launch(Dispatchers.IO) {
initAnalytics()
initCrashReporting()
warmupImageCache()
}
}
}
Rendering Performance
List Performance
// Android: Use RecyclerView with DiffUtil
class OrderAdapter : ListAdapter<Order, OrderViewHolder>(OrderDiffCallback()) {
// DiffUtil calculates minimal changes, avoiding full list re-renders
}
class OrderDiffCallback : DiffUtil.ItemCallback<Order>() {
override fun areItemsTheSame(old: Order, new: Order) = old.id == new.id
override fun areContentsTheSame(old: Order, new: Order) = old == new
}
// iOS: Use LazyVStack, not VStack for large lists
ScrollView {
LazyVStack { // Only renders visible items
ForEach(orders) { order in
OrderRow(order: order)
}
}
}
Image Loading
// Coil (Android) - Efficient image loading
AsyncImage(
model = ImageRequest.Builder(context)
.data(product.imageUrl)
.crossfade(true)
.size(Size(300, 300)) // Downscale to view size
.memoryCachePolicy(CachePolicy.ENABLED)
.diskCachePolicy(CachePolicy.ENABLED)
.build(),
contentDescription = product.name
)
Memory Management
Common Memory Leaks
// LEAK: Activity reference in singleton
object Analytics {
var activity: Activity? = null // Never cleared!
}
// FIX: WeakReference or no reference
object Analytics {
fun track(context: Context, event: String) {
val appContext = context.applicationContext // App context, not activity
// ...
}
}
// LEAK: Strong reference cycle in closure
class ViewModel {
var completion: (() -> Void)?
func load() {
service.fetch {
self.updateUI() // Strong reference to self!
}
}
}
// FIX: Weak self
func load() {
service.fetch { [weak self] in
self?.updateUI()
}
}
Network Efficiency
// Batch network requests
suspend fun syncAll() {
// BAD: 20 individual requests
orders.forEach { api.syncOrder(it) }
// GOOD: 1 batch request
api.batchSync(orders)
}
// Compress request/response
val client = OkHttpClient.Builder()
.addInterceptor(GzipInterceptor())
.build()
Profiling Tools
| Platform | Tool | Measures |
|---|---|---|
| iOS | Instruments (Time Profiler) | CPU, memory, allocations |
| iOS | Instruments (Core Animation) | Frame rate, GPU usage |
| Android | Android Profiler | CPU, memory, network, energy |
| Android | Systrace / Perfetto | Frame rendering, thread scheduling |
| Both | Firebase Performance | Startup time, network latency |
Anti-Patterns
| Anti-Pattern | Consequence | Fix |
|---|---|---|
| Heavy work on main thread | UI freezes, dropped frames | Move to background thread |
| Full-size images in thumbnails | Excessive memory usage | Downscale to view size |
| No image caching | Re-download every time | Memory + disk cache |
| Eager initialization | Slow startup | Lazy init + deferred loading |
| No memory leak detection | OOM crashes | LeakCanary (Android), Instruments (iOS) |
Mobile performance optimization is about respecting constraints — CPU, memory, battery, and network — that you cannot change. Profile first, optimize what the data tells you, and measure the impact of every change.