Mobile App Performance Optimization Guide
Introduction
Performance is critical for mobile app success. Users expect instant responses, smooth animations, and minimal battery drain. This guide covers essential optimization techniques for React Native, Flutter, iOS, and Android apps.
1. Startup Time Optimization
// React Native - Lazy load screens
import React, { lazy, Suspense } from 'react';
const HomeScreen = lazy(() => import('./screens/HomeScreen'));
const ProfileScreen = lazy(() => import('./screens/ProfileScreen'));
function App() {
return (
<Suspense fallback={<LoadingScreen />}>
<Navigation />
</Suspense>
);
}
// Android - Lazy initialization
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
// Delay non-critical initialization
lifecycleScope.launch {
delay(100)
initializeAnalytics()
initializeCrashReporting()
}
}
}
// iOS - Optimize app delegate
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Delay non-critical setup
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.setupAnalytics()
self.setupCrashReporting()
}
return true
}
2. Bundle Size Reduction
// React Native - Enable Hermes
// android/app/build.gradle
project.ext.react = [
enableHermes: true
]
// Use ProGuard for Android
android {
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
// Flutter - Build optimized release
flutter build apk --release --target-platform android-arm64
flutter build ios --release
// Analyze bundle size
npx react-native-bundle-visualizer
// Remove unused dependencies
npm prune --production
yarn autoclean --force
3. Image Optimization
// React Native - Fast Image
import FastImage from 'react-native-fast-image';
<FastImage
source={{
uri: 'https://example.com/image.jpg',
priority: FastImage.priority.normal,
cache: FastImage.cacheControl.immutable
}}
style={{ width: 200, height: 200 }}
resizeMode={FastImage.resizeMode.cover}
/>
// Use WebP format
// Android automatically supports WebP
// iOS requires manual implementation
// Image caching strategy
const imageCache = new Map();
async function loadImage(url) {
if (imageCache.has(url)) {
return imageCache.get(url);
}
const image = await fetch(url);
imageCache.set(url, image);
return image;
}
// Lazy load images
<LazyLoadImage
src="image.jpg"
placeholder={<Skeleton />}
threshold={100}
/>
4. List Rendering Optimization
// React Native - FlatList optimization
<FlatList
data={items}
renderItem={renderItem}
keyExtractor={item => item.id}
// Performance props
removeClippedSubviews={true}
maxToRenderPerBatch={10}
updateCellsBatchingPeriod={50}
windowSize={10}
initialNumToRender={10}
// Use getItemLayout for fixed height items
getItemLayout={(data, index) => ({
length: ITEM_HEIGHT,
offset: ITEM_HEIGHT * index,
index
})}
/>
// Flutter - ListView.builder
ListView.builder(
itemCount: items.length,
cacheExtent: 100.0,
itemBuilder: (context, index) {
return ListTile(
title: Text(items[index].title),
);
},
)
// Android - RecyclerView with DiffUtil
class MyAdapter : ListAdapter<Item, ViewHolder>(DiffCallback()) {
class DiffCallback : DiffUtil.ItemCallback<Item>() {
override fun areItemsTheSame(old: Item, new: Item) = old.id == new.id
override fun areContentsTheSame(old: Item, new: Item) = old == new
}
}
// iOS - Prefetch data sources
extension MyViewController: UICollectionViewDataSourcePrefetching {
func collectionView(_ collectionView: UICollectionView,
prefetchItemsAt indexPaths: [IndexPath]) {
indexPaths.forEach { indexPath in
// Prefetch data for cells
}
}
}
5. Memory Management
// React Native - Memory leaks prevention
useEffect(() => {
const subscription = eventEmitter.addListener('event', handler);
return () => {
subscription.remove(); // Cleanup
};
}, []);
// Avoid memory leaks with timers
useEffect(() => {
const timer = setInterval(() => {
// Do something
}, 1000);
return () => clearInterval(timer);
}, []);
// Android - Lifecycle aware components
class MyViewModel : ViewModel() {
private val _data = MutableLiveData<String>()
val data: LiveData<String> = _data
override fun onCleared() {
super.onCleared()
// Cleanup resources
}
}
// iOS - Weak references
class MyViewController: UIViewController {
var observer: NSObjectProtocol?
deinit {
if let observer = observer {
NotificationCenter.default.removeObserver(observer)
}
}
}
6. Network Optimization
// Request batching
class APIBatcher {
private queue = [];
private timer = null;
add(request) {
this.queue.push(request);
if (!this.timer) {
this.timer = setTimeout(() => {
this.flush();
}, 50);
}
}
flush() {
if (this.queue.length > 0) {
fetch('/api/batch', {
method: 'POST',
body: JSON.stringify({ requests: this.queue })
});
this.queue = [];
this.timer = null;
}
}
}
// Request caching
const cache = new Map();
async function fetchWithCache(url, maxAge = 300000) {
const cached = cache.get(url);
if (cached && Date.now() - cached.timestamp < maxAge) {
return cached.data;
}
const data = await fetch(url).then(r => r.json());
cache.set(url, { data, timestamp: Date.now() });
return data;
}
// Prefetch critical data
useEffect(() => {
const prefetch = async () => {
await Promise.all([
fetchWithCache('/api/user'),
fetchWithCache('/api/settings'),
]);
};
prefetch();
}, []);
7. Animation Performance
// React Native - Use native driver
Animated.timing(fadeAnim, {
toValue: 1,
duration: 300,
useNativeDriver: true // Enable native driver
}).start();
// Avoid expensive operations during animations
const animateAndUpdate = useCallback(() => {
Animated.timing(value, {
toValue: 100,
useNativeDriver: true
}).start(() => {
// Update state after animation completes
setCompleted(true);
});
}, []);
// Flutter - Use const constructors
const MyWidget({
Key? key,
required this.title,
}) : super(key: key);
// Android - Use hardware acceleration
<application
android:hardwareAccelerated="true">
// iOS - Optimize layer rendering
view.layer.shouldRasterize = true
view.layer.rasterizationScale = UIScreen.main.scale
8. Code Splitting and Lazy Loading
// React Native - Dynamic imports
const loadScreen = async (screenName) => {
const screen = await import(`./screens/${screenName}`);
return screen.default;
};
// Flutter - Deferred loading
import 'package:myapp/screens/settings.dart' deferred as settings;
void loadSettings() async {
await settings.loadLibrary();
Navigator.push(
context,
MaterialPageRoute(builder: (_) => settings.SettingsScreen())
);
}
// Android - Dynamic feature modules
// build.gradle
dynamicFeatures = [':feature_settings', ':feature_profile']
// Load dynamic feature
SplitInstallManager manager = SplitInstallManagerFactory.create(context);
SplitInstallRequest request = SplitInstallRequest.newBuilder()
.addModule("feature_settings")
.build();
manager.startInstall(request);
9. Database Optimization
// SQLite - Use indexes
CREATE INDEX idx_user_email ON users(email);
CREATE INDEX idx_post_user ON posts(user_id);
// Batch operations
db.transaction(() => {
for (const item of items) {
db.executeSql('INSERT INTO items VALUES (?)', [item]);
}
});
// Room - Use suspend functions
@Dao
interface UserDao {
@Query("SELECT * FROM users WHERE id = :id")
suspend fun getUserById(id: Int): User
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertUsers(users: List<User>)
}
// Core Data - Batch operations
let batchInsert = NSBatchInsertRequest(
entity: User.entity(),
objects: usersData
)
try context.execute(batchInsert)
10. Monitoring and Profiling
// React Native - Performance monitoring
import perf from '@react-native-firebase/perf';
const trace = await perf().startTrace('custom_trace');
await doSomething();
await trace.stop();
// Flutter - Performance overlay
MaterialApp(
showPerformanceOverlay: true,
home: HomeScreen(),
)
// Android - Profiler usage
// Use Android Studio Profiler:
// - CPU Profiler for method tracing
// - Memory Profiler for heap analysis
// - Network Profiler for API calls
class PerformanceMonitor {
fun measureExecutionTime(block: () -> Unit) {
val start = System.currentTimeMillis()
block()
val duration = System.currentTimeMillis() - start
Log.d("Performance", "Execution time: $duration ms")
}
}
// iOS - Instruments
import os.signpost
let log = OSLog(subsystem: "com.myapp", category: "Performance")
os_signpost(.begin, log: log, name: "DataLoad")
loadData()
os_signpost(.end, log: log, name: "DataLoad")
11. Battery Optimization
// Reduce location updates frequency
navigator.geolocation.watchPosition(
position => handlePosition(position),
error => handleError(error),
{
enableHighAccuracy: false, // Use network instead of GPS
distanceFilter: 10, // Only update every 10 meters
maximumAge: 30000 // Accept cached positions
}
);
// Batch background tasks
// Android - WorkManager
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresCharging(true)
.build()
val work = OneTimeWorkRequestBuilder<SyncWorker>()
.setConstraints(constraints)
.build()
// iOS - Background fetch
func application(_ application: UIApplication,
performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
syncData { success in
completionHandler(success ? .newData : .noData)
}
}
💡 Best Practices:
- Profile before optimizing - measure impact
- Optimize the critical path first
- Use native modules for CPU-intensive tasks
- Implement proper caching strategies
- Lazy load non-critical resources
- Monitor performance in production
- Test on low-end devices
- Reduce main thread work
- Optimize images and assets
- Use pagination for large datasets
Conclusion
Mobile app performance directly impacts user satisfaction and retention. By implementing these optimization techniques, you'll create faster, more responsive applications that provide excellent user experiences across all devices.