Flutter mobil uygulama geliştirme projelerinde JSON ayrıştırma, şifreleme ve görsel dönüşümlerini Isolate’lara taşıyarak ana isolate üzerindeki frame gecikmesini ölçmeyi ve yönetmeyi inceleyin.
Flutter Mobil Uygulama Geliştirmede Isolate ile CPU İşleri Tasarımı
Flutter mobil uygulama geliştirmede Isolate sınırını doğru çizmek
Bir Isolate, ayrı heap ve event loop ile çalışır; dolayısıyla ana isolate’taki Dart nesnelerine paylaşımlı bellek üzerinden erişemez. Bu ayrım, büyük bir REST yanıtını ayrıştırma, ZIP açma, PBKDF2/bcrypt dışı saf Dart hesapları veya görüntü piksel dönüşümü gibi CPU-bound işler için uygundur. HTTP isteğinin kendisi genellikle asenkron I/O olduğundan onu Isolate’a taşımak çoğu durumda fayda sağlamaz; socket beklerken ana isolate bloklanmaz. Flutter mobil uygulama geliştirme sırasında önce işi CPU-bound, I/O-bound ve platform-plugin bağımlı olarak sınıflandırın; `BuildContext`, `MethodChannel` ve widget nesnelerini worker’a göndermeyin.
Tek seferlik, saf bir dönüşüm için `Isolate.run` çağrısı, `compute` benzeri bir kullanım sağlar. Gönderilen `Map`/`List` grafiği worker’a kopyalanır; 20 MB’lık JSON metnini hem `String` hem ayrıştırılmış `Map` olarak taşımak iki ayrı bellek maliyeti oluşturabilir. Binary veri için `TransferableTypedData`, sahipliği transfer ederek büyük `Uint8List` kopyasını önler. Bu API ile yalnızca top-level veya statik fonksiyonlara ve serileştirilebilir girdilere bağlı kalın.
import 'dart:isolate';
import 'dart:typed_data';
Future<Uint8List> invertPixels(Uint8List source) async {
final transferable = TransferableTypedData.fromList([source]);
return Isolate.run(() {
final bytes = transferable.materialize().asUint8List();
for (var i = 0; i < bytes.length; i += 4) {
bytes[i] = 255 - bytes[i]; // R
bytes[i + 1] = 255 - bytes[i + 1]; // G
bytes[i + 2] = 255 - bytes[i + 2]; // B
}
return bytes;
});
}Buradaki incelik şudur: 4K bir RGBA buffer yaklaşık 33 MB’tır; her kullanıcı etkileşiminde yeni isolate başlatmak, spawn ve mesajlaşma maliyetini işin kendisinden pahalı hâle getirebilir. Sürekli işlerde her istek için `Isolate.run` yerine uzun ömürlü bir worker tasarlayın. Ayrıca platform eklentilerinin çoğu ana isolate’a bağlı messenger kullanır; worker içinden bir plugin çağrısının çalışacağını varsaymak yerine, plugin sonucunu ana isolate’ta alıp saf veriyi worker’a iletin.
Uzun ömürlü worker için Port tabanlı iş protokolü
Uzun süren işlerde `SendPort` ve `ReceivePort` ile açık bir istek/yanıt protokolü kurun. Aşağıdaki örnek her iş için bir `requestId` taşır; bu detay önemlidir çünkü birden fazla işin sonucu sıralı dönmek zorunda değildir. Worker hata verdiğinde `onError` portunu dinlemek, aksi hâlde UI tarafında tamamlanmayan `Completer` bırakılmasını önler.
import 'dart:async';
import 'dart:isolate';
class HashWorker {
late final Isolate _isolate;
late final SendPort _workerPort;
final _responses = ReceivePort();
final _errors = ReceivePort();
final _pending = <int, Completer<int>>{};
var _nextId = 0;
Future<void> start() async {
final ready = ReceivePort();
_isolate = await Isolate.spawn(
_entry,
ready.sendPort,
onError: _errors.sendPort,
errorsAreFatal: false,
);
_workerPort = await ready.first as SendPort;
_responses.listen((message) {
final id = message[0] as int;
_pending.remove(id)?.complete(message[1] as int);
});
_errors.listen((error) {
for (final completer in _pending.values) {
completer.completeError(StateError('worker error: $error'));
}
_pending.clear();
});
}
Future<int> checksum(List<int> bytes) {
final id = _nextId++;
final completer = Completer<int>();
_pending[id] = completer;
_workerPort.send([id, bytes, _responses.sendPort]);
return completer.future;
}
void dispose() {
_isolate.kill(priority: Isolate.immediate);
_responses.close();
_errors.close();
}
static void _entry(SendPort readyPort) {
final inbox = ReceivePort();
readyPort.send(inbox.sendPort);
inbox.listen((message) {
final id = message[0] as int;
final bytes = message[1] as List<int>;
final replyTo = message[2] as SendPort;
var sum = 0;
for (final byte in bytes) sum = (sum + byte) & 0xffffffff;
replyTo.send([id, sum]);
});
}
}İptal için `Future` iptali yoktur; iş protokolüne `cancel(requestId)` mesajı ekleyin ve worker’ın büyük döngülerde, örneğin her 16.384 öğede, iptal kümesini kontrol etmesini sağlayın. Tek bir dev görüntü dönüşümünü bölmeden çalıştırmak iptal mesajlarının event queue’da beklemesine neden olur. Worker sayısını da cihaz çekirdek sayısına eşitlemek yerine 1-2 ile başlayıp ölçün: aynı anda çok sayıda CPU worker çalıştırmak render thread ile çekirdek rekabeti yaratır.
Flutter state management katmanında worker sonuçlarını birleştirmek
Flutter state management katmanına `SendPort`, `ReceivePort` veya mutable buffer sızdırmak yerine, worker sonucunu immutable bir domain modeli olarak yayınlayın. Örneğin Riverpod `AsyncNotifier`, ekran kapandığında `ref.onDispose(worker.dispose)` ile worker yaşam döngüsünü bağlayabilir. Bu yaklaşım, sonuç geç geldiyse dispose edilmiş bir sayfaya state yazılması sorununu merkezi olarak engeller.
class ReportNotifier extends AsyncNotifier<Report> {
final _worker = HashWorker();
@override
Future<Report> build() async {
await _worker.start();
ref.onDispose(_worker.dispose);
return _load();
}
Future<Report> _load() async {
final bytes = await ref.read(reportRepositoryProvider).downloadBytes();
final checksum = await _worker.checksum(bytes);
return Report(byteCount: bytes.length, checksum: checksum);
}
Future<void> refresh() async {
state = const AsyncLoading();
state = await AsyncValue.guard(_load);
}
}Bu örnekte `downloadBytes()` ana isolate’ta kalır; ağ I/O zaten event loop üzerinden bekletilir. Sadece checksum CPU işi worker’a gider. `flutter state management` seçiminden bağımsız kritik kural, her progress mesajında tüm sayfa state’ini yeniden üretmemektir. Worker ilerlemeyi yüzde bazında iletiyorsa UI güncellemelerini 50-100 ms aralığında throttle edin; aksi durumda sık state bildirimi widget build maliyetini yeniden ana isolate’a taşır. Bu ayrım, dart programlama eğitimi içinde `Future` ile paralel CPU yürütmenin farklı kavramlar olduğunu göstermek için de iyi bir pratik örnektir.
Arka plan görevi ile Isolate arasındaki platform sınırı
Isolate, uygulama işlemi yaşıyorsa CPU işini ayırır; uygulama öldürüldükten sonra işletim sisteminin görevi tekrar başlatacağı anlamına gelmez. Zamanlanmış senkronizasyon için `workmanager` gibi bir eklenti kullanıldığında callback ayrı bir Flutter engine/isolate bağlamında başlayabilir. Callback’i tree-shaking’den korumak için top-level fonksiyon ve `@pragma('vm:entry-point')` kullanın; `BuildContext` veya aktif Riverpod container’ı burada varsaymayın.
import 'package:workmanager/workmanager.dart';
@pragma('vm:entry-point')
void backgroundDispatcher() {
Workmanager().executeTask((taskName, inputData) async {
// Burada bağımlılıkları yeniden kurun: HTTP client, token deposu, repository.
final repository = SyncRepository.createForBackground();
await repository.syncPendingChanges();
return true; // Başarı/yeniden deneme kararını eklentiye bildirir.
});
}
Future<void> configureBackgroundWork() async {
await Workmanager().initialize(backgroundDispatcher);
await Workmanager().registerPeriodicTask(
'pending-change-sync',
'syncPendingChanges',
constraints: Constraints(networkType: NetworkType.connected),
);
}Periyodik işlerin tam dakikasında çalışacağına güvenmeyin: Android tarafında scheduler kotaları ve pil politikaları, iOS tarafında ise arka plan yürütme bütçesi zamanlamayı erteleyebilir. Bu nedenle senkronizasyonu idempotent tasarlayın: yerel outbox kaydına benzersiz `operationId` koyun, sunucuya idempotency anahtarı gönderin ve başarılı işlemden sonra kaydı transaction içinde silin. Cross platform mobil uygulama geliştirme hedefinde doğru ortak payda, 'belirli bir zamanda kesin çalışır' değil, uygulama yeniden açıldığında ve sistem izin verdiğinde güvenle devam eder olmalıdır.
DevTools ile Isolate kazancını before-after olarak ölçmek
Bir Isolate değişikliğini yalnızca hissedilen akıcılıkla onaylamayın. Ölçümü fiziksel cihazda `flutter run --profile` ile yapın; debug moddaki JIT, assertion ve servis maliyetleri frame sürelerini temsil etmez. Flutter DevTools Performance görünümünde aynı 10 MB fixture ile iki akış kaydedin: önce JSON ayrıştırmayı ana isolate’ta, sonra worker’da çalıştırın. Karşılaştırmada yalnız toplam süreyi değil, UI thread'deki uzun Dart event’lerini, raster/UI frame sürelerini ve 16,67 ms bütçesini aşan frame sayısını kaydedin.
import 'dart:developer' as developer;
Future<T> traced<T>(String name, Future<T> Function() action) async {
final task = developer.TimelineTask()..start(name);
final watch = Stopwatch()..start();
try {
return await action();
} finally {
watch.stop();
task.finish(arguments: {'elapsedMs': watch.elapsedMilliseconds});
}
}
// Önce: traced('parse-main', () async => parseJson(raw));
// Sonra: traced('parse-worker', () => Isolate.run(() => parseJson(raw)));Fixture boyutu, cihaz modeli, tekrar sayısı ve p95 değerini rapora yazın; örneğin 30 tekrar yapıp ilk 5 çalıştırmayı warm-up olarak dışarıda bırakın. DevTools CPU Profiler'da `parseJson` görünmüyorsa işin worker isolate’ında olduğundan emin olun ve ilgili isolate seçin. Sık yapılan hata, yalnız worker süresini ölçüp seri hale getirme/kopyalama maliyetini dışarıda bırakmaktır; `traced` çağrısını mesaj hazırlanmasından sonuç state’e yazılmasına kadar sarın. Bir flutter kursu veya flutter eğitimi laboratuvarında bu before-after düzeni, 'Isolate her işi hızlandırır' varsayımını somut trace verisiyle sınamanın en güvenilir yoludur.
İlgili Eğitim
YTÜSEM İlgili Eğitim
Sık Sorulan Sorular
Flutter eğitiminde compute yerine Isolate.run ne zaman kullanılmalı?
Tek seferlik, saf ve CPU-bound bir fonksiyon için `Isolate.run(() => transform(input))` yeterlidir. İş sık tekrarlanıyorsa spawn maliyetini azaltmak için `Isolate.spawn` ile uzun ömürlü worker kurun. Her iki durumda da `BuildContext`, açık database handle veya MethodChannel nesnesi göndermeyin; yalnız serileştirilebilir veri ya da büyük binary için `TransferableTypedData` gönderin.
Flutter state management ile worker progress güncellemeleri nasıl yönetilir?
Riverpod, Bloc veya başka bir katmanda progress mesajlarını immutable DTO olarak alın ve 50-100 ms throttle uygulayın. Her byte bloğunda state yayınlamak yerine worker’ın örneğin her 16.384 kayıtta bir progress göndermesi, build fırtınasını önler. Sayfa veya provider dispose olduğunda `Isolate.kill` ve port kapatma işlemlerini yaşam döngüsüne bağlayın.
Cross platform mobil uygulama geliştirmede Isolate arka planda garantiyle çalışır mı?
Hayır. Isolate yalnız mevcut uygulama işlemi içindeki Dart yürütmesini ayırır. Uygulama arka planda veya öldürülmüşken zamanlanmış iş için `workmanager` benzeri platform köprüsü, ağ kısıtı ve idempotent senkronizasyon gerekir. İşletim sistemi görevi geciktirebileceği için outbox kaydı, idempotency anahtarı ve yeniden deneme stratejisi uygulayın.
Dart programlama eğitiminde Isolate performansı nasıl ölçülür?
Fiziksel cihazda `flutter run --profile` çalıştırın, DevTools Performance ile ana isolate ve worker isolate timeline’larını ayrı inceleyin. Aynı fixture üzerinde ana-isolate ve worker sürümlerini en az 30 kez çalıştırın; toplam uçtan uca süre, p95 frame süresi ve 16,67 ms üzerindeki frame sayısını birlikte karşılaştırı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.


