• 4.09.2026 21:12:40
  • Admin Admin

Flutter mobil uygulama geliştirme projelerinde büyük JSON, sıkıştırılmış veri ve hesaplama yükünü UI isolate'ından ayırın. Bu yazı, flutter state management akışında Isolate.run, veri aktarımı, iptal ve DevTools ölçümünü ele alır.

Flutter State Management'de Isolate ile Veri Hatti Tasarimi

Flutter state management icin UI isolate sinirini dogru cizmek

Bir ekranın state'i `loading -> ready` geçerken ana isolate üzerinde 8-20 MB JSON ayrıştırmak, Dart kodu asenkron yazılmış olsa bile frame üretimini durdurabilir. `await jsonDecode(...)` işlemi event loop'u serbest bırakmaz; sadece Future tamamlanana kadar çağıranı bekletir. Bu nedenle flutter state management katmanında ağ isteği, CPU ayrıştırması ve state yayını üç ayrı aşama olmalıdır: HTTP istemcisi byte alır, worker byte'ı modele çevirir, notifier yalnızca sonucu state'e yazar. Bu sınırı doğrulamak için Flutter DevTools Performance görünümünde bir kullanıcı etkileşimi kaydedin; uzun `Frame` blokları yanında CPU Profiler'da `JsonUtf8Decoder` veya model mapleme fonksiyonunuz görünüyorsa iş ana isolate'tadır.

`compute` küçük ve seyrek işler için okunabilir bir sarmalayıcıdır, ancak her çağrıda yeni isolate oluşturma maliyetini gizler. Tek seferlik bir içe aktarma veya büyük yanıt ayrıştırma için `Isolate.run` kullanın. Fonksiyonun isolate sınırından geçen tüm verisi sendable olmalıdır; `BuildContext`, `StreamSubscription`, açık dosya tanıtıcısı ve çoğu plugin nesnesi gönderilemez. Aşağıdaki örnekte HTTP gövdesi `TransferableTypedData` ile taşınır; bu tip büyük byte tamponlarında kopyalama maliyetini azaltmak için tasarlanmıştır.

import 'dart:convert';
import 'dart:isolate';
import 'dart:typed_data';

class Order {
  const Order(this.id, this.totalCents);
  final String id;
  final int totalCents;
}

Future<List<Order>> parseOrders(Uint8List responseBytes) async {
  final payload = TransferableTypedData.fromList([responseBytes]);
  return Isolate.run(() => _decodeOrders(payload));
}

List<Order> _decodeOrders(TransferableTypedData payload) {
  final bytes = payload.materialize().asUint8List();
  final rows = jsonDecode(utf8.decode(bytes)) as List<dynamic>;
  return rows
      .cast<Map<String, dynamic>>()
      .map((row) => Order(row['id'] as String, row['totalCents'] as int))
      .toList(growable: false);
}

Buradaki incelik, `TransferableTypedData.materialize()` çağrısından sonra tamponun worker tarafında sahiplenilmesidir. Aynı payload'ı iki farklı işe yeniden kullanmaya çalışmayın. Ayrıca `jsonDecode` sonucunda dönen büyük `Map` ve `List` ağacını geri göndermek yine serileştirme maliyeti doğurur. 50.000 kayıt gibi hacimlerde UI'ya tüm DTO listesini göndermek yerine worker'ın sıralama, filtreleme ve normalizasyon yapıp ekrana gerekli alanları içeren daha küçük immutable satırlar döndürmesi ölçülebilir bir fark yaratır. Bu ayrım, flutter eğitimi sırasında genellikle atlanan ama üretimde heap kopyalarını belirleyen noktadır.

Flutter mobil uygulama gelistirme icin kalici worker ve plugin kanallari

Her 200 ms'de gelen telemetri paketi için `Isolate.run` çağırmak doğru eşik değildir: spawn, port kurulumu ve mesaj serileştirmesi iş süresinden pahalı hale gelebilir. Sık gelen işler için uygulama açılışında `Isolate.spawn` ile kalıcı bir worker kurun, her mesaja `jobId` ekleyin ve en fazla iki bekleyen iş kabul eden bir kuyruk uygulayın. Kuyruk dolduğunda telemetri gibi son değer yeterli verilerde eski işi atmak, her örneği işlemekten daha doğru bir gecikme politikasıdır.

Worker içinde `path_provider`, `shared_preferences` veya başka bir platform channel kullanan bir plugin çağrılacaksa yalnızca `Isolate.spawn` yetmez. Başlatan isolate'tan alınan `RootIsolateToken` ile binary messenger'ı bağlamak ve plugin kaydını çalıştırmak gerekir. Aksi halde worker'ın saf Dart kodu çalışırken plugin çağrısı `MissingPluginException` ile sonuçlanabilir. Giriş fonksiyonunu ağaç sarsma işleminden korumak için `@pragma('vm:entry-point')` ekleyin.

import 'dart:isolate';
import 'package:flutter/services.dart';

class WorkerConfig {
  const WorkerConfig(this.rootToken, this.replyPort);
  final RootIsolateToken rootToken;
  final SendPort replyPort;
}

@pragma('vm:entry-point')
void workerMain(WorkerConfig config) {
  BackgroundIsolateBinaryMessenger.ensureInitialized(config.rootToken);
  DartPluginRegistrant.ensureInitialized();
  config.replyPort.send('worker-ready');
}

Future<Isolate> startWorker(SendPort replyPort) {
  final token = RootIsolateToken.instance!;
  return Isolate.spawn(workerMain, WorkerConfig(token, replyPort));
}

Bu tasarım cross platform mobil uygulama geliştirme bağlamında Android ve iOS için aynı Dart iş hattını sağlar, fakat plugin davranışını otomatik olarak eşitlemez. Özellikle UI lifecycle'ına veya foreground activity'ye bağlı plugin'leri worker'a taşımadan önce kendi platform belgelerindeki isolate desteğini kontrol edin. Worker kapanışında ana isolate `SendPort` üzerinden bir `shutdown` mesajı göndermeli, worker `ReceivePort.close()` çağırmalı ve sahibi de `isolate.kill(priority: Isolate.immediate)` yerine normal kapanış için kısa bir süre tanımalıdır; ani öldürme yarım kalmış dosya yazımını bırakabilir.

Flutter state management akısinda eski sonucu ekrana yazdirmamak

Isolate kullanmak yarış koşulunu ortadan kaldırmaz. Kullanıcı arama metnini art arda değiştirdiğinde ilk sorgunun CPU işi ikinci sorgudan sonra bitebilir ve eski listeyi yeni state'in üzerine yazabilir. Riverpod, Bloc, ChangeNotifier veya kendi reducer'ınız fark etmeksizin state geçişine monoton bir nesil numarası ekleyin. Aşağıdaki ChangeNotifier örneği, tamamlanan işin başladığı neslin hala güncel olup olmadığını kontrol eder; bu kontrol olmadan debounce yalnızca istek sayısını düşürür, sonuç sırasını garanti etmez.

class OrdersController extends ChangeNotifier {
  int _generation = 0;
  OrdersState state = const OrdersState.idle();

  Future<void> refresh(Uint8List body) async {
    final generation = ++_generation;
    state = const OrdersState.loading();
    notifyListeners();

    final orders = await parseOrders(body);
    if (generation != _generation) return;

    state = OrdersState.ready(orders);
    notifyListeners();
  }
}

Bu kontrol UI yazımını engeller ama başlamış `Isolate.run` işini iptal etmez. CPU tüketimi kritikse kalıcı worker protokolüne `jobId` ve `latestRequestedJobId` ekleyin; worker pahalı bir döngüde her 500-1000 kayıtta yeni iş gelip gelmediğini kontrol ederek eski işi erken bırakabilir. HTTP tarafında ise Dart `HttpClient` veya kullandığınız istemcinin gerçek iptal mekanizmasını ayrıca çağırın. Örneğin dio kullanan bir istemcide her aramaya `CancelToken` bağlamak, yalnızca state sonucunu saklamak yerine ağ soketini de serbest bırakır. Bu ayrım, dart programlama eğitimi içeriğinde Future iptali ile gerçek işlem iptalinin aynı şey olmadığını göstermek için önemlidir.

DevTools ile once-sonra isolate maliyetini olcmek

Bir isolate değişikliğini yalnızca hissiyatla kabul etmeyin. Aynı release veya profile derlemesini, aynı fiziksel cihazı, aynı 10 MB yanıt dosyasını ve aynı kullanıcı akışını kullanarak iki kayıt alın. İlk kayıtta ayrıştırmayı ana isolate'ta bırakın, ikinci kayıtta `Isolate.run` sürümünü çalıştırın. Flutter DevTools Performance ekranında ilgili interaction çevresindeki UI ve raster frame sayılarını, CPU Profiler'da `jsonDecode` örneklerini ve Memory ekranında geçici heap tepesini karşılaştırın. 60 Hz cihazda 16.67 ms, 120 Hz cihazda 8.33 ms frame aralığı olduğundan rapora cihaz yenileme hızını da yazın; tek bir sabit frame bütçesi kullanmayın.

Ölçüm işaretlerini Dart DevTools zaman çizelgesinde ayrıştırma sınırıyla ilişkilendirmek için `Timeline.timeSync` kullanın. İşaretin kendisi düşük maliyetlidir, ancak çok sık çağrılırsa timeline'ı gürültülü yapar; sadece iş başlangıcı ve bitişinde kullanın.

import 'dart:developer' as developer;

Future<List<Order>> tracedParse(Uint8List bytes) {
  return developer.Timeline.timeSync(
    'orders.parse.in_worker',
    () => parseOrders(bytes),
    arguments: {'bytes': bytes.length},
  );
}

Karar verirken p50 yerine p95 veya p99 etkileşim gecikmesini raporlayın. Örneğin 30 tekrar sonunda ana isolate sürümünde p95 UI frame süresi 19 ms, worker sürümünde 7 ms ise kazanım anlaşılır; fakat worker sürümünde toplam sonuç hazır olma süresi 45 ms uzadıysa küçük 20 KB yanıtlar için eşik koymak mantıklıdır. Bu nedenle byte uzunluğuna göre politika uygulayın: örneğin 64 KB altını ana isolate'ta, üzerini worker'da çalıştırın ve bu eşiği kendi cihaz ölçümünüzle doğrulayın. İyi bir flutter kursu laboratuvarı, bu p95 tablosunu CI artefaktı olarak saklar; cihaz, veri boyutu ve commit bilgisi olmadan tek sayı karşılaştırması anlamlı değildir.

Isolate tasariminda bellek ve hata sinirlari

Worker'a gönderilen nesne grafiğini küçük tutun. `Uint8List.sublistView` ile büyük bir buffer'ın küçük parçasını işlemek cazip görünse de isolate mesajlaşmasında alttaki büyük buffer'ın nasıl taşındığını ölçmeden bunu bellek optimizasyonu saymayın. Sadece gerekli byte aralığını yeni bir `Uint8List` içine kopyalamak, 100 MB'lık bir ana tamponun yaşam süresini uzatmaktan daha ucuz olabilir. Memory görünümünde external memory ve old-gen artışını, en az beş ardışık içe aktarma sonrası gözlemleyin.

Hata kanalını iş protokolünün parçası yapın. `Isolate.run` hatayı Future üzerinden geri verir, ancak kalıcı worker'da `onError` ve `onExit` portları bağlanmazsa worker öldüğünde UI tarafı sonsuza kadar loading state'inde kalabilir. `Isolate.spawn` çağrısında ayrı `ReceivePort` nesnelerini `onError` ve `onExit` olarak verin; hata geldiğinde aktif `jobId` için hata state'i yayınlayın, exponential backoff ile en fazla üç yeniden başlatma deneyin ve bozuk girdiyi yeniden kuyruğa koymayın. Bu sınır, flutter mobil uygulama geliştirme uygulamalarında hatalı sıkıştırılmış dosyanın sürekli worker çökertmesine karşı somut bir korumadır.

İlgili Eğitim

Flutter Eğitimi

Sık Sorulan Sorular

Flutter state management icinde compute mi Isolate.run mi kullanilmali?

Tek seferlik, saf Dart ve büyük CPU işi için ikisi de ayrı isolate kullanır; `Isolate.run` dönüş tipi ve hata akışını doğrudan Future ile yönetmeyi kolaylaştırır. İş 100-200 ms aralıklarla geliyorsa her çağrının spawn maliyetini DevTools CPU Profiler ile ölçün ve kalıcı `Isolate.spawn` worker'ına geçin. Plugin veya BuildContext'i hiçbirine argüman olarak göndermeyin.

Flutter mobil uygulama gelistirme projesinde JSON parse isolate'a ne zaman tasinmali?

Aynı cihaz, profile derleme ve sabit yanıt gövdesiyle önce ana isolate sürümünü kaydedin. DevTools'ta `jsonDecode` sırasında UI frame'leri yenileme aralığını aşıyorsa worker adaydır. Sonra `Isolate.run` ve `TransferableTypedData` sürümünde p95 UI frame süresini, toplam sonuç gecikmesini ve heap tepesini karşılaştırın; küçük gövdeler için spawn maliyeti daha büyük olabilir.

Cross platform mobil uygulama gelistirme icin worker isolate'ta plugin cagrisi nasil yapilir?

`Isolate.spawn` girişine ana isolate'tan `RootIsolateToken.instance!` aktarın, worker girişinde `BackgroundIsolateBinaryMessenger.ensureInitialized(token)` ve `DartPluginRegistrant.ensureInitialized()` çağırın. Ardından kullandığınız plugin'in isolate desteğini platform bazında doğrulayın. UI activity gerektiren plugin'leri worker'a taşımak yerine sonucu worker'da hesaplayıp plugin çağrısını ana isolate'ta yapın.

Dart programlama egitimi sirasinda Future iptali neden yeterli degildir?

Bir Future sonucunu dinlemeyi bırakmak, çalışmakta olan CPU döngüsünü veya HTTP soketini otomatik durdurmaz. Ağ için istemcinin iptal API'sini, kalıcı worker için `jobId` tabanlı erken bırakma kontrolünü kullanın. State katmanında ayrıca nesil numarası kontrolü yaparak eski işin geç tamamlanmasının yeni ekran state'ini ezmesini önleyin.

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