• 24.09.2026 21:39:26
  • Admin Admin

Flutter mobil uygulama geliştirme projelerinde platform channel çağrılarını ölçmeyi, mesaj sayısını azaltmayı ve native iş yükünü UI iş parçacığından ayırmayı somut Flutter, Kotlin ve DevTools adımlarıyla ele alın.

Flutter Mobil Uygulama Geliştirmede Platform Channel Gecikmesi

Flutter mobil uygulama geliştirmede gecikmeyi çağrı bazında ölçün

Bir platform channel sorunu, Dart tarafındaki await süresinin uzun olması değildir; bu süre codec serileştirmesi, engine mesaj kuyruğu, native handler ve Android veya iOS API çağrısını birlikte içerir. Bu nedenle önce Flutter DevTools Performance görünümünde gerçek cihazla profile modda bir akış kaydedin. Dart tarafında her kritik çağrıyı TimelineTask ile işaretleyin; DevTools'ta bu işaretin süresini frame timeline ile aynı anda inceleyin. Bu yaklaşım, örneğin 18 ms süren bir çağrının raster işinden mi yoksa native tarafa geçişten mi kaynaklandığını ayırır. flutter eğitimi veya dart programlama eğitimi sırasında bu ayrım genellikle atlanır ve yalnızca Stopwatch sonucu üzerinden yanlış teşhis konur.

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

const _deviceChannel = MethodChannel('com.acme.device/info');

Future readDeviceSnapshot() async {
  final task = TimelineTask(filterKey: 'platform-channel');
  task.start('deviceSnapshot');
  try {
    final result = await _deviceChannel.invokeMapMethod<String, dynamic>(
      'readSnapshot',
      {'keys': ['battery', 'networkType', 'locale']},
    );
    return result ?? const {};
  } finally {
    task.finish();
  }
}

Ölçümü tekrarlanabilir yapın: uygulama açıldıktan sonra 20 ısınma çağrısını atın, ardından aynı fiziksel cihazda 100 çağrının P50 ve P95 değerini hesaplayın. Soğuk başlangıçtaki ilk çağrı, sınıf yükleme ve sistem servis bağlantısı içerdiği için sıcak yol bütçesine dahil edilmemelidir. Android tarafında Dart zaman çizelgesini native kesitlerle eşlemek için android.os.Trace.beginSection() kullanın; gerekirse adb shell perfetto -o /data/misc/perfetto-traces/channel.perfetto-trace -t 10s sched freq ve ardından adb pull ile scheduler gecikmesini doğrulayın. Değişiklikten önce ve sonra aynı 100 çağrılık senaryoda P95, çağrı sayısı ve ana iş parçacığı meşguliyetini karşılaştırın.

Cross platform mobil uygulama geliştirme için mesaj sözleşmesini küçültün

En sık görülen maliyet, tek bir ekranda üç veya dört küçük invokeMethod çağrısı yapmaktır. Her çağrı ayrı mesaj zarfı, StandardMessageCodec kodlama çözme işlemi ve platform kuyruğu geçişi üretir. Bunun yerine ekrana ait veriyi tek bir snapshot sözleşmesinde toplayın. Aşağıdaki değişiklikte çağrı sayısı üçten bire iner; önce-sonra kıyasını yalnızca toplam süreyle değil, DevTools timeline'daki deviceSnapshot event sayısıyla da doğrulayın.

// Once: 3 ayri platform gecisi
final battery = await _deviceChannel.invokeMethod('battery');
final network = await _deviceChannel.invokeMethod('networkType');
final locale = await _deviceChannel.invokeMethod('locale');

// Sonra: tek mesaj, acik anahtar listesi
final snapshot = await _deviceChannel.invokeMapMethod<String, dynamic>(
  'readSnapshot',
  {'keys': ['battery', 'networkType', 'locale']},
);

Sözleşmeyi Map<String, dynamic> olarak yıllarca büyütmek yerine Pigeon ile tipli host API üretin. Komutu CI içinde sabitleyin: dart run pigeon --input pigeons/device_api.dart. Pigeon, Dart ve Kotlin veya Swift tarafındaki method adlarını ve alan tiplerini aynı şemadan üretir; bu, native tarafın Int döndürüp Dart tarafında double beklemesi gibi ancak çalışma anında görülen codec hatalarını derleme aşamasına yaklaştırır. Yine de Pigeon otomatik batching yapmaz; readSnapshot(List<String> keys) gibi bir API tasarlamak gerekir.

Büyük veya sık veri için JSON string göndermeyin. JSON, önce native nesneden UTF-8 metne, sonra Dart nesnesine ikinci kez ayrıştırma gerektirir ve geçici bellek üretir. Sayısal telemetri aktarımında BasicMessageChannel<ByteData> ile BinaryCodec ve sürüm alanı olan sabit bir ikili şema kullanın. Edge case: byte order'ı iki platformda açıkça Endian.little veya Endian.big seçmeden bırakırsanız ARM cihazlarındaki testleriniz sorunsuz görünse bile başka bir üreticideki native kod farklı yorumlayabilir.

Native handler'ı ana iş parçacığı bütçesinden çıkarın

Android embedding'de varsayılan MethodChannel handler'ı çoğunlukla ana iş parçacığıyla ilişkilidir. Handler içinde diskten okuma, PackageManager üzerinden paket tarama veya senkron Binder çağrısı yapmak, platform tarafında frame kaçırılmasına yol açar. Android plugin'inizde arka plan task queue oluşturun ve pahalı I/O işini burada başlatın. Ancak bu queue sıralıdır, sınırsız paralel worker havuzu değildir; aynı channel'a uzun bir iş koyarsanız sonraki mesajlar yine bekler.

val messenger = flutterEngine.dartExecutor.binaryMessenger
val queue = messenger.makeBackgroundTaskQueue()
val channel = MethodChannel(
    messenger,
    "com.acme.device/info",
    StandardMethodCodec.INSTANCE,
    queue,
)

channel.setMethodCallHandler { call, result ->
    when (call.method) {
        "readSnapshot" -> {
            Trace.beginSection("DevicePlugin.readSnapshot")
            try {
                val snapshot = mapOf(
                    "battery" to batteryReader.readPercent(),
                    "networkType" to networkReader.currentType(),
                    "locale" to Locale.getDefault().toLanguageTag(),
                )
                result.success(snapshot)
            } catch (t: Throwable) {
                result.error("DEVICE_READ_FAILED", t.message, null)
            } finally {
                Trace.endSection()
            }
        }
        else -> result.notImplemented()
    }
}

Bu değişiklikten önce ve sonra Perfetto'da main thread track'ini inceleyin: hedef, DevicePlugin.readSnapshot kesitinin UI thread yerine background task queue üzerinde görünmesidir. Bunun istisnası UI API'leridir. Android'de View, iOS'ta UIKit erişimi ana iş parçacığında kalmalıdır; bunları arka queue'ya taşımak gecikmeyi azaltmaz, yarış koşulu veya platform hatası üretir. Doğru desen, UI'dan sadece gerekli anlık değeri ana thread'de almak, dosya ve ağ I/O'sunu arka tarafta yapmak ve sonucu channel ile dönmektir.

Flutter state management katmanında platform erişimini tekilleştirin

flutter state management katmanının doğrudan MethodChannel çağırması, aynı provider veya bloc'un birden fazla widget tarafından izlenmesinde yinelenen native istekler doğurur. Channel erişimini repository içinde toplayın, kısa ömürlü bir TTL cache ekleyin ve state katmanına yalnızca tipli model döndürün. Aşağıdaki örnekte 500 ms içinde gelen talepler aynı Future'ı paylaşır; bu, hızlı route geçişlerinde beş ayrı battery sorgusu gönderilmesini engeller.

class DeviceRepository {
  DeviceRepository(this._channel);
  final MethodChannel _channel;
  Future<DeviceSnapshot>? _inFlight;
  DateTime? _expiresAt;
  DeviceSnapshot? _cached;

  Future<DeviceSnapshot> getSnapshot() {
    final now = DateTime.now();
    if (_cached != null && _expiresAt != null && now.isBefore(_expiresAt!)) {
      return Future.value(_cached);
    }
    return _inFlight ??= _load().whenComplete(() => _inFlight = null);
  }

  Future<DeviceSnapshot> _load() async {
    final raw = await _channel.invokeMapMethod<String, dynamic>('readSnapshot');
    final value = DeviceSnapshot.fromMap(raw ?? const {});
    _cached = value;
    _expiresAt = DateTime.now().add(const Duration(milliseconds: 500));
    return value;
  }
}

Buradaki incelik hata durumudur: başarısız Future'ı cache'lemeyin ve _inFlight alanını mutlaka whenComplete ile temizleyin. Aksi halde bir kere hata veren platform servisi, uygulama yaşam döngüsü boyunca aynı başarısız Future'ı tekrar döndürebilir. flutter kursu projelerinde görülen bir diğer hata, uygulama background'a döndükten sonra cache'i geçerli saymaktır. AppLifecycleListener ile resumed olayında TTL'yi sıfırlayın; ağ türü, izin ve locale gibi değerler uygulama askıdayken değişebilir.

Platform channel bütçesini entegrasyon testinde kilitleyin

Manuel profil kaydı regresyonları güvenilir biçimde yakalayamaz. integration_test ile gerçek plugin'in çalıştığı emülatör veya fiziksel cihazda sıcak çağrı maliyetini test edin. Testi debug modda yorumlamayın; debug JIT, servis protokolü ve assertion maliyeti channel sonucunu bozar. CI'da örneğin flutter drive --driver=test_driver/integration_test.dart --target=integration_test/channel_benchmark_test.dart --profile komutunu sabit cihaz imajında çalıştırın.

testWidgets('snapshot P95 stays below the channel budget', (tester) async {
  final repo = DeviceRepository(const MethodChannel('com.acme.device/info'));
  for (var i = 0; i < 20; i++) {
    await repo.getSnapshot();
  }

  final samples = <int>[];
  for (var i = 0; i < 100; i++) {
    final watch = Stopwatch()..start();
    await repo.getSnapshot();
    samples.add(watch.elapsedMicroseconds);
  }
  samples.sort();
  final p95 = samples[(samples.length * 0.95).floor() - 1];
  expect(p95, lessThan(8000));
});

Bu testte cache açıksa her tur native çağrı yapmaz; benchmark için repository'ye test amaçlı forceRefresh parametresi ekleyin veya TTL'yi sıfır yapın. Ayrıca 8 ms sınırını örnek bir bütçe olarak ele alın, evrensel eşik değildir: mevcut sürümünüzün P95 değerini baseline olarak kaydedin, batching değişikliğinden sonra aynı cihazda yeniden ölçün ve kabul eşiğini ürün akışının frame bütçesine göre belirleyin. Bu disiplin, cross platform mobil uygulama geliştirme sürecinde Android ve iOS plugin'lerinin ayrı ayrı yavaşlamasını görünür kılar.

İlgili Eğitim

Flutter Eğitimi

Sık Sorulan Sorular

Flutter mobil uygulama geliştirmede MethodChannel gecikmesi nasıl ölçülür?

Dart çağrısını TimelineTask ile sarın, profile modda DevTools Performance kaydı alın ve 20 ısınma sonrası 100 çağrının P50/P95 değerlerini çıkarın. Native handler'a Android Trace.beginSection veya iOS os_signpost ekleyerek aynı zaman aralığını platform izinde eşleyin.

Flutter state management içinde platform channel doğrudan kullanılmalı mı?

Provider, Bloc veya notifier içinde doğrudan channel kullanmak yerine repository katmanı kurun. Repository, aynı anda gelen talepler için tek in-flight Future paylaşmalı, hata sonrası bu Future'ı temizlemeli ve yaşam döngüsü değişiminde geçersizleşen TTL cache'i sıfırlamalıdır.

Cross platform mobil uygulama geliştirme için Pigeon ne zaman kullanılmalı?

Birden çok method, iç içe veri modeli veya Dart-native tip uyumsuzluğu riski varsa Pigeon kullanın. dart run pigeon --input pigeons/device_api.dart komutunu CI'a ekleyin. Ancak Pigeon mesaj sayısını azaltmaz; yüksek frekanslı veri için ayrıca batch API veya ByteData tabanlı ikili protokol tasarlayın.

Flutter eğitimi sırasında platform channel performansı için hangi araçlar kullanılmalı?

Flutter DevTools Performance, Android Perfetto ve native Trace.beginSection birlikte kullanılmalıdır. DevTools Dart tarafındaki await aralığını, Perfetto ise ana thread'de bloklama ve scheduler beklemesini gösterir; yalnızca Stopwatch kullanmak native kuyrukta geçirilen süreyi açıklamaz.

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