ESC
Type to search guides, tutorials, and reference documentation.
Verified by Garnet Grid

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

MetricGoodAcceptablePoor
Cold start time< 1s1-2s> 3s
Frame rate60 fps (16ms)45 fps< 30 fps
Memory usage< 100 MB100-200 MB> 300 MB
App size< 30 MB30-100 MB> 200 MB
Battery impactBackground: < 1%/hr1-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

PlatformToolMeasures
iOSInstruments (Time Profiler)CPU, memory, allocations
iOSInstruments (Core Animation)Frame rate, GPU usage
AndroidAndroid ProfilerCPU, memory, network, energy
AndroidSystrace / PerfettoFrame rendering, thread scheduling
BothFirebase PerformanceStartup time, network latency

Anti-Patterns

Anti-PatternConsequenceFix
Heavy work on main threadUI freezes, dropped framesMove to background thread
Full-size images in thumbnailsExcessive memory usageDownscale to view size
No image cachingRe-download every timeMemory + disk cache
Eager initializationSlow startupLazy init + deferred loading
No memory leak detectionOOM crashesLeakCanary (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.

Jakub Dimitri Rezayev
Jakub Dimitri Rezayev
Founder & Chief Architect • Garnet Grid Consulting

Jakub holds an M.S. in Customer Intelligence & Analytics and a B.S. in Finance & Computer Science from Pace University. With deep expertise spanning D365 F&O, Azure, Power BI, and AI/ML systems, he architects enterprise solutions that bridge legacy systems and modern technology — and has led multi-million dollar ERP implementations for Fortune 500 supply chains.

View Full Profile →