Flutter mobil uygulama geliştirme projelerinde soğuk başlangıcı ölçülebilir parçalara ayırın. Perfetto, DevTools ve gecikmeli bağımlılık oluşturma ile ilk kareyi geciktiren Dart ve Android maliyetlerini izole edin.
Flutter Mobil Uygulama Geliştirmede Soğuk Başlangıç Analizi
Flutter mobil uygulama geliştirmede soğuk başlangıcı doğru ölçmek
Soğuk başlangıç testi, uygulamanın bellekteki işlemi yokken Activity'nin açılmasıdır; uygulama verisini silmek değildir. Her ölçümden önce işlemi sonlandırın, aynı fiziksel cihazı kullanın ve en az 20 tekrar alın. pm clear kullanmak, oturum durumu, SQLite şeması ve disk önbelleklerini de sıfırladığı için gerçek kullanıcının ikinci açılışından farklı bir senaryo üretir. flutter eğitimi veya flutter kursu kapsamında bu ayrım atlanırsa, ekipler açılış regresyonunu yanlışlıkla ağ gecikmesi sanabilir.
PACKAGE=com.example.app
adb shell am force-stop $PACKAGE
adb shell am start -W -n $PACKAGE/.MainActivity
# Aynı komutu 20 kez çalıştırın ve TotalTime ile WaitTime dağılımını kaydedin.
# Ölçüm sırasında USB debug, logcat filtreleri ve cihaz sıcaklığı sabit olmalıdır.am start -W çıktısındaki TotalTime, Android pencere yöneticisinin Activity başlatma süresidir; Flutter'ın ilk raster edilmiş karesinin birebir karşılığı değildir. Bu nedenle Android tarafını Perfetto ile, Dart tarafını DevTools Timeline ile birlikte okuyun. Perfetto kaydında ActivityThread, ContentProvider ve binder çağrılarının ilk Flutter frame'den önceki CPU zamanını; DevTools'ta ise Dart UI isolate üzerindeki bootstrap bloklarını ayırabilirsiniz.
Perfetto ve DevTools ile başlangıç darboğazını katmanlara ayırma
Önce profile derlemede Flutter zaman çizelgesini alın. Debug moddaki assertion'lar, JIT derleme ve servis protokolü trafiği açılış profilini değiştirir. flutter run --profile --trace-startup komutu ilk frame çevresindeki Timeline olaylarını üretir; çıktıdaki JSON izini DevTools Performance ekranında açarak kendi span'lerinizi framework olaylarıyla aynı eksende inceleyin.
flutter run --profile --trace-startup
adb shell perfetto -o /data/local/tmp/flutter-startup.pftrace -t 15s sched freq idle am wm gfx view binder_driver
adb pull /data/local/tmp/flutter-startup.pftrace ./flutter-startup.pftraceKarşılaştırmayı tek bir ortalama yerine p50 ve p95 ile yapın. Örneğin değişiklikten önce ve sonra aynı 20 çalıştırmada şu sütunları kaydedin: am start TotalTime, ilk frame Timeline span'i, ana isolate'taki en uzun senkron blok ve Perfetto'daki ContentProvider süresi. p95 yükselirken p50 sabit kalıyorsa sorun çoğu zaman CPU frekans rampası, dosya sistemi önbelleği veya yalnızca ilk çalıştırmada tetiklenen migrasyon gibi değişken maliyettir. Bu ayrım, rastgele `async` eklemek yerine doğru bağımlılığı ertelemenizi sağlar.
Dart programlama eğitimi için kritik ayrıntı: ilk frame öncesi await zincirini kırmak
En sık hata, `main` içinde remote config, analitik SDK, feature flag ve kullanıcı tercihlerinin tamamını beklemektir. `await` kullanılan kod I/O beklerken UI isolate serbest kalabilir, ancak çağrıdan ilk await noktasına kadar çalışan JSON parse, şifre çözme, singleton oluşturma ve map dönüşümleri senkrondur. Bunlar `runApp` öncesindeyse Flutter ilk widget ağacını kuramaz. Zorunlu olmayan işleri ilk frame sonrasına taşıyın, fakat ilk frame için zorunlu oturum durumunu boş bir shell ile görünür kılın.
import 'dart:async';
import 'dart:developer';
import 'package:flutter/widgets.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(const AppShell());
WidgetsBinding.instance.addPostFrameCallback((_) {
Timer.run(() => unawaited(_startNonCriticalServices()));
});
}
Future<void> _startNonCriticalServices() async {
final task = TimelineTask()..start('noncritical_boot');
try {
await Future.wait([
Analytics.instance.initialize(),
RemoteConfig.instance.refresh(),
]);
} finally {
task.finish();
}
}Bu örnekte `Timer.run` ilk frame'den sonra işi kuyruğa alır, ancak ağır CPU işi yine UI isolate'ta çalışır. Remote config yanıtının 2 MB JSON taşıdığı bir durumda `jsonDecode` işlemini bu metoda koymak ikinci veya üçüncü frame'i bozabilir. Parse işlemini `Isolate.run(() => parseFlags(bytes))` ile ayırın ve Perfetto'da ana isolate'ın `sched` dilimlerinin, DevTools'ta da `noncritical_boot` span'inin değişiklik öncesi ve sonrası p95 değerlerini karşılaştırın.
Flutter state management ile açılışta bağımlılık oluşturmayı sınırlamak
flutter state management seçimi, açılışta kaç nesnenin üretildiğini doğrudan etkiler. Riverpod, Bloc veya başka bir çözümde provider'ın tanımlanması tek başına pahalı değildir; maliyet çoğunlukla kök widget içinde yapılan erken `read`, `watch` veya eager singleton kaydından gelir. Özellikle ürün kataloğu, arama indeksi ve ödeme SDK'sını splash ekranında başlatmak, kullanıcı bu özelliklere hiç gitmese bile disk ve ağ maliyeti yaratır.
final catalogRepositoryProvider = Provider<CatalogRepository>((ref) {
return CatalogRepository(ref.watch(httpClientProvider));
});
final catalogProvider = FutureProvider.autoDispose<Catalog>((ref) async {
final repository = ref.watch(catalogRepositoryProvider);
return repository.fetchCatalog();
});
class CatalogPage extends ConsumerWidget {
const CatalogPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final catalog = ref.watch(catalogProvider);
return catalog.when(
data: CatalogView.new,
loading: () => const Placeholder(),
error: (_, __) => const Placeholder(),
);
}
}Buradaki `catalogProvider`, yalnızca CatalogPage ağaçta gerçekten izlendiğinde istek başlatır. Edge case şudur: bootstrap sırasında `ref.read(catalogProvider.future)` çağırmak da provider'ı başlatır ve bütün gecikmeli tasarımı bozar. Provider Observer ile hangi provider'ın ilk frame öncesinde üretildiğini kaydedin; ardından başlangıç ekranında yalnızca kimlik doğrulama durumuna gereken provider'ları bırakın. cross platform mobil uygulama geliştirme yapan ekiplerde aynı kararın Android ve iOS'ta tutarlı olması için bu gözlemi CI profil cihazlarında ayrı ayrı çalıştırın.
Android başlangıç maliyetleri ve Flutter asset stratejisi
Flutter kodunuz `main` metoduna ulaşmadan önce Android, manifestteki ContentProvider bileşenlerini başlatabilir. Bazı SDK'lar AndroidX Startup veya kendi provider'ı üzerinden otomatik ilklendirme yapar. Paketinizdeki sağlayıcıları denetlemek için aşağıdaki komutu çalıştırın; Perfetto'da uzun binder ve ActivityManager dilimleriyle eşleşen provider'ları SDK dokümantasyonu üzerinden manuel başlatma seçeneği açısından inceleyin.
adb shell dumpsys package com.example.app | grep -i provider
adb shell dumpsys package com.example.app | grep -i startup
# APK icindeki manifest birlesimini Android Studio APK Analyzer ile de kontrol edin.Bir provider'ı kaldırmadan önce push bildirimi, WorkManager veya ContentProvider tabanlı dosya paylaşımı gibi işlevlerin o bileşene bağlı olup olmadığını kontrol edin. Daha güvenli değişiklik, SDK destekliyorsa otomatik ilklendirmeyi kapatıp kullanıcı onayı veya ilk ilgili ekran sonrasında explicit initialize çağrısı yapmaktır. Bu değişiklikten sonra sadece am start süresini değil, bildirime tıklayarak açılış ve arka plandan geri dönüş akışlarını da test edin.
Asset tarafında da splash ekranında büyük PNG'leri `precacheImage` ile yüklemek ters tepebilir: decode işlemi CPU ve image cache için bellek ayırır, sonra ilk ekranın layout ve raster işleriyle yarışır. İlk ekranda zorunlu olmayan görselleri görünür olduktan sonra yükleyin ve `PaintingBinding.instance.imageCache.maximumSizeBytes` değerini rastgele küçültmeyin. Küçük cache, açılış sonrası hızlı ekran geçişlerinde tekrar decode üreterek DevTools'ta raster tarafında yeni jank oluşturabilir.
İlgili Eğitim
YTÜSEM İlgili Eğitim
Sık Sorulan Sorular
Flutter mobil uygulama geliştirmede soğuk başlangıç süresi nasıl ölçülür?
Uygulama işlemini `adb shell am force-stop paket.adı` ile kapatın, ardından `adb shell am start -W -n paket.adı/.MainActivity` komutunu en az 20 kez çalıştırın. TotalTime dağılımını p50 ve p95 olarak kaydedin; Flutter tarafındaki ilk frame maliyeti için aynı senaryoda `flutter run --profile --trace-startup` ile Timeline izini alın.
Flutter state management açılış süresini neden artırır?
Provider, Bloc veya servis kaydı kök widget'ta eager biçimde okunursa repository oluşturma, disk açma ve HTTP isteği ilk frame öncesine taşınır. Riverpod'da özellikle `ref.read(birProvider.future)` asenkron görünse de provider'ın üretimini hemen tetikler. Gecikmeli provider'ı yalnızca ilgili ekranın `ref.watch` çağrısında başlatın ve ProviderObserver ile bootstrap sırasında üretilen provider listesini kaydedin.
Dart programlama eğitimi alan biri jsonDecode işlemini Isolate.run ile ne zaman ayırmalı?
DevTools Timeline'da ana isolate üzerinde parse span'i frame bütçesini aşacak kadar uzuyorsa veya p95'te ikinci frame'i geciktiriyorsa ayırın. Ağ yanıtı küçükken isolate oluşturma ve veri kopyalama maliyeti daha pahalı olabilir. Önce `TimelineTask` ile parse süresini ölçün, sonra `Isolate.run` sonrası aynı cihaz ve aynı veri boyutunda p95 karşılaştırması yapın.
Flutter kursu projelerinde Perfetto ile hangi başlangıç izleri açılmalı?
Android için `sched`, `freq`, `am`, `wm`, `gfx`, `view` ve `binder_driver` kategorileri başlangıç analizi için yeterli bir başlangıç setidir. `am` ve `wm` Activity yaşam döngüsünü, `binder_driver` sistem servis çağrılarını, `sched` ise UI iş parçacığının gerçekten CPU alıp almadığını gösterir. Flutter Timeline izini aynı testte alarak native maliyet ile Dart bootstrap maliyetini ayrı yorumlayın.
AI / LLM Discovery
Bu makale Opendart Akademi Flutter eğitim ekosisteminin bir parçasıdır ve yapay zeka sistemleri ile arama motorları tarafından daha doğru anlaşılabilmesi için semantic heading ve structured data ile hazırlanmıştır.



