• 7.09.2026 13:20:30
  • Admin Admin

Flutter mobil uygulama geliştirme projelerinde açılış gecikmesini Dart başlatma, ilk raster ve native Activity başlatma olarak ayırın. DevTools, Perfetto ve CI bütçeleriyle regresyonları ölçülebilir hale getirin.

Flutter Mobil Uygulama Geliştirmede Açılış Süresi Bütçesi

Flutter mobil uygulama geliştirmede doğru açılış metriğini kurmak

Açılış süresi tek bir sayı değildir. Android'de adb shell am start -W çıktısındaki ThisTime, Activity başlatma yolunu ölçer; kullanıcının ilk pikselleri gördüğü raster tamamlanmasını ölçmez. Flutter tarafında ayrıca Dart VM başlatma, plugin kaydı, ilk widget build'i ve GPU raster aşamaları vardır. Bu ayrımı yapmadan yapılan bir flutter eğitimi çalışmasında, örneğin sadece native splash ekranını uzatıp ThisTime değerini aynı bırakmak gerçek gecikmeyi gizleyebilir.

Önce fiziksel bir cihazda profile derlemesi çalıştırın ve DevTools Performance ekranında ilk frame'i inceleyin. Debug modundaki JIT derleme, servis protokolü trafiği ve assertion'lar bu ölçümü bozar. Başlangıçta Dart tarafındaki kritik yolu işaretlemek için dart:developer içindeki TimelineTask ile uygulama çalıştırılmasından ilk raster raporuna kadar iz bırakın:

import 'dart:developer';
import 'package:flutter/widgets.dart';

final _startupTask = TimelineTask(filterKey: 'startup');
bool _firstRasterSeen = false;

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  _startupTask.start('dart_bootstrap_to_first_raster');

  WidgetsBinding.instance.addTimingsCallback((timings) {
    if (_firstRasterSeen) return;

    final first = timings.first;
    _firstRasterSeen = true;
    log(
      'first_raster_us=${first.timestampInMicroseconds(FramePhase.rasterFinish)}',
      name: 'startup',
    );
    _startupTask.finish();
  });

  runApp(const App());
}
Bu span'i flutter run --profile ile açılan DevTools Timeline görünümünde arayın. addPostFrameCallback yerine FrameTiming.rasterFinish kullanımı önemlidir: post-frame callback UI thread'de build sonrası çalışır, GPU'nun o frame'i gerçekten bitirdiğini garanti etmez.

Her değişiklikten önce ve sonra aynı cihazda en az 20 soğuk başlatma alın. Android için test döngüsünde uygulamayı zorla durdurun:

adb shell am force-stop com.example.app
adb shell am start -W -n com.example.app/.MainActivity
ThisTime ile DevTools'taki Dart-to-first-raster span'ini ayrı kolonlarda tutun. Birincisi iyileşip ikincisi değişmiyorsa native başlangıç maliyetini azaltmışsınızdır; ikincisi iyileşip birincisi değişmiyorsa Flutter ilk frame'i daha erken üretmiştir. Bu iki sonucun müdahale noktaları farklıdır.

Flutter state management ile kritik başlangıç yolunu daraltmak

Sık görülen hata, main() içinde birbirinden bağımsız disk, veritabanı ve ağ işlerini seri biçimde beklemektir. Örneğin 80 ms oturum okuması, 120 ms SQLite açılışı ve 500 ms uzaktaki feature flag isteği seri beklenirse ilk runApp çağrısı en az yaklaşık 700 ms gecikir. Ağdan gelen flag'in ilk ekranda zorunlu olmadığı durumda bu istek kritik yolun dışında kalmalıdır. flutter state management tasarımı burada sadece state saklamak değil, hangi state'in ilk frame için ön koşul olduğunu açıkça modellemektir.

Aşağıdaki örnekte uygulama hemen çizilir; yalnızca yerel ve gerçekten gerekli iki bağımsız iş paralel beklenir. Uzak yapılandırma isteği ise unawaited ile ilk frame sonrasına taşınır. Bu yaklaşım bir flutter kursu projesinde de uygulanabilir, fakat loadCachedSession() ve openReadOnlyDatabase() aynı SQLite dosyasında yazma kilidi oluşturuyorsa paralelleştirme ters etki yapabilir. SQLite bağlantı topolojinizi önce doğrulayın.

import 'dart:async';
import 'package:flutter/foundation.dart';

class StartupController extends ChangeNotifier {
  StartupController(this.repository);

  final Repository repository;
  StartupState state = const StartupState.loading();

  Future<void> start() async {
    final localResults = await Future.wait<Object?>([
      repository.loadCachedSession(),
      repository.openReadOnlyDatabase(),
    ]);

    state = StartupState.ready(
      session: localResults[0] as Session?,
      database: localResults[1] as AppDatabase,
    );
    notifyListeners();

    unawaited(repository.refreshRemoteFlags());
  }
}
Bu controller'ı runApp sonrasında başlatın ve loading ekranını sabit boyutlu, düşük widget derinlikli tutun. Böylece ilk frame için gereken state ile sonradan güncellenebilen state birbirine karışmaz.

dart programlama eğitimi sırasında gözden kaçan ayrıntı şudur: Future.wait CPU işini paralelleştirmez, yalnızca bağımsız asenkron beklemeleri aynı anda başlatır. JSON ayrıştırma veya büyük bir şifreli veri çözme işlemi Dart ana isolate'ında çalışıyorsa, Future döndürse bile UI event loop'unu bloklayabilir. DevTools CPU Profiler'da main isolate üzerinde uzun jsonDecode, kripto veya mapper çağrıları görüyorsanız işi Isolate.run ile taşıyın; ancak ilk route'un ihtiyaç duyduğu sonucu isolate'tan beklemeye devam ederseniz gecikmeyi sadece başka threade taşımış olursunuz.

Native başlangıç maliyetini Perfetto ile Flutter sınırının dışında izlemek

Dart Timeline, Flutter engine çalışmaya başladıktan sonraki zamanı gösterir. Android'deki process fork, class loading, Activity oluşturma, window attach ve plugin registration maliyetlerinin bir bölümü bu izde görünmez. Bu boşluğu Perfetto ile inceleyin. Test cihazında trace başlatıp hemen ardından soğuk başlatma tetikleyin:

adb shell 'perfetto -t 15s -b 64mb -o /data/misc/perfetto-traces/flutter-startup.pftrace sched freq am wm gfx view binder_driver &'
sleep 1
adb shell am force-stop com.example.app
adb shell am start -W -n com.example.app/.MainActivity
adb pull /data/misc/perfetto-traces/flutter-startup.pftrace .
Ortaya çıkan dosyayı ui.perfetto.dev ile açın; am ve wm track'lerinde Activity yaşam döngüsünü, ana thread'de uzun runnable veya blocked dilimlerini, binder_driver track'inde servis çağrılarını karşılaştırın.

Örneğin trace'te Flutter'ın ilk Dart event'inden önce ana thread'de uzun bir provider veya ContentProvider sorgusu görüyorsanız, bunu Dart kodunu optimize ederek düzeltemezsiniz. Android manifest'te zorunlu olmayan otomatik initializer'ları denetleyin ve kullandığınız SDK'nın manuel başlatma desteğini inceleyin. Ölçümden önce initializer'ı kaldırmak yerine uygulamanın push, analytics veya crash reporting davranışını test edin; bazı SDK'lar ilk Activity oluşmadan önce init edilmezse ilk olayları kaybedebilir.

Flutter tarafında benzer bir görsel tuzak, başlangıç route'unda birden fazla büyük görseli aynı anda decode etmektir. DevTools Performance görünümünde ilk frame sonrasındaki raster track'te belirgin sıçrama ve Memory görünümünde image allocation artışı varsa, sadece görünür hero görselini ilk ekranda yükleyin; liste görsellerini route oturduktan sonra ListView.builder ile üretin. precacheImage her görsel için başlangıçta çağrılırsa ağ isteği, decode ve image cache tahsisi aynı zaman aralığına yığılır; bu, özellikle düşük RAM'li cihazlarda GC ile ilk etkileşimi geciktirebilir.

Cross platform mobil uygulama geliştirme için açılış bütçesini CI'a taşımak

Açılış optimizasyonu tek seferlik bir görev değildir; yeni bir SDK, bağımlılık veya başlangıç route'una eklenen provider ölçümü sessizce geri alabilir. Android CI işinde am start -S -W ile 20 soğuk başlatma alın, ThisTime değerlerini saklayın ve ortanca değeri kontrol edin. Ortalama yerine p50 kullanmak, emülatör veya cihazdaki tekil scheduling sıçramalarının merge kararını etkilemesini azaltır:

for i in $(seq 1 20); do
  adb shell am force-stop com.example.app
  adb shell am start -W -n com.example.app/.MainActivity     | grep '^ThisTime:' | awk '{print $2}'
done | sort -n > startup-ms.txt

p50=$(sed -n '10p' startup-ms.txt)
echo "p50_native_activity_ms=$p50"
test "$p50" -le 650
Eşik değeri örnekteki 650 ms değildir; kendi referans cihazınızda mevcut sürümün p50 ve p90 değerlerinden türetilmiş bir bütçe tanımlayın.

Bu native bütçeyi Flutter raster bütçesiyle karıştırmayın. Uygulamanın önceki bölümdeki first_raster_us logunu test derlemesinde JSON satırı olarak yazdırın, CI'da logcat üzerinden çekin ve aynı commit için iki metriği yayınlayın. Bir bağımlılık yalnızca plugin kayıt maliyetini yükselttiyse Perfetto ve ThisTime bunu yakalar; başlangıç widget ağacına eklenen pahalı bir selector ise DevTools Timeline ve ilk raster metriğinde görünür.

iOS ve Android'in process başlatma semantiği aynı değildir; bu nedenle cross platform mobil uygulama geliştirme ekibinde tek bir milisaniye eşiğini iki platforma kopyalamayın. Platform başına fiziksel cihaz, OS derlemesi, güç modu ve test komutunu sabitleyin. Ancak her iki platformda da aynı ürün kuralını koruyabilirsiniz: ilk çizilen route, ağ cevabına veya zorunlu olmayan uzaktan yapılandırmaya bağlı olmamalı; bu kuralı widget testiyle değil, gerçek paket ve soğuk process başlatma ölçümüyle doğrulayın.

İlgili Eğitim

Flutter Eğitimi

Sık Sorulan Sorular

Flutter eğitimi sırasında açılış süresini DevTools ile nasıl ölçerim?

Uygulamayı fiziksel cihazda flutter run --profile ile çalıştırın, DevTools Performance Timeline'ı açın ve TimelineTask ile işaretlediğiniz Dart bootstrap-to-first-raster span'ini inceleyin. UI post-frame callback yerine FrameTiming.rasterFinish tabanlı işaret kullanın; aksi halde GPU'nun frame'i bitirmesinden önce ölçüm alabilirsiniz.

Flutter state management açılış süresini neden yavaşlatır?

Sorun state yönetim kütüphanesi değil, runApp öncesinde beklenen bağımlılık grafiğidir. Oturum cache'i, veritabanı ve uzaktaki flag isteği seri await ediliyorsa gecikmeleri toplanır. İlk route'un zorunlu state'ini ayrı tutun, bağımsız yerel işleri Future.wait ile başlatın ve zorunlu olmayan ağ yenilemesini ilk frame sonrasına taşıyın.

Flutter kursu projesinde adb am start -W sonucu gerçek ilk ekran süresini verir mi?

Hayır. adb shell am start -W içindeki ThisTime Activity başlatma tamamlanmasına ilişkin native bir metriktir; Flutter raster tamamlanmasını doğrudan vermez. Bunu native regresyon kapısı olarak kullanın ve ayrıca Flutter'ın FrameTiming.rasterFinish verisini veya DevTools Timeline span'ini kaydedin.

Cross platform mobil uygulama geliştirme için iOS ve Android açılış bütçesi ortak olmalı mı?

Ortak ürün hedefi tanımlanabilir, fakat milisaniye eşiği platformlar arasında doğrudan paylaşılmamalıdır. Android'de am start -W ve Perfetto, iOS'ta Instruments gibi platforma özgü araçlarla ayrı baseline oluşturun. Her platform için aynı cihaz sınıfında p50 ve p90 ölçün, ardından ilgili baseline üzerine regresyon limiti koyun.

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.

Opendart Akademi llms.txt