Uygulama

Android’da Uygulama İçin En Etkili Offline Veri Senkronizasyonu Rehberi

Offline Veri Senkronizasyonunun Temel Mantığı

Neden böyle yapılmalı? Kullanıcıların internet bağlantısının kesildiği anlarda bile veri kaybı yaşamaması, uygulama deneyimini kesintisiz kılar ve kullanıcı sadakatini artırır. Ayrıca, ağ maliyetlerini düşürerek sunucu üzerindeki yükü dengelemeye yardımcı olur.

Hangi hatalar yapılıyor? Çoğu geliştirici sadece bir “queue” sistemi kurar fakat veri çakışmalarını ve yeniden deneme mantığını göz ardı eder. Bu durum, aynı kaynağa birden fazla güncelleme gönderildiğinde tutarsızlık yaratır.

Pratik adımlarla nasıl uygulanır? İlk adım, yerel bir veri deposu (Room, SQLite) seçmek ve bu depoya bir PendingOperation tablosu eklemektir. Bu tablo, sunucuya gönderilmesi beklenen tüm CRUD işlemlerini tutar. İkinci adım, bir WorkManager işi tanımlayarak ağ mevcut olduğunda bu kuyrukları otomatik olarak işlemek ve çakışma kontrolü yapmak gerekir.

Yerel Veri Katmanı Oluşturma

Room Database ile Model Tasarımı

Neden böyle yapılmalı? Room, SQLite’ın üzerine tip güvenliği ve compile‑time sorgu doğrulaması ekleyerek veri bütünlüğünü korur. Bu sayede hatalı sorgular uygulama derlemesinde yakalanır.

Hangi hatalar yapılıyor? Entity sınıflarında nullable alanları gereksiz yere bırakmak, veri tutarlılığını bozar ve null pointer hatalarına yol açar. Ayrıca, indekslerin eksik tanımlanması sorgu performansını düşürür.

Pratik adımlarla nasıl uygulanır?

@Entity(tableName = "tasks",
        indices = {@Index(value = {"remote_id"}, unique = true)})
public class Task {
    @PrimaryKey(autoGenerate = true)
    public long localId;
    public String remoteId; // sunucudan gelen benzersiz id
    public String title;
    public boolean completed;
    public long updatedAt; // epoch millis
}

Bu yapı, yerel ve uzak kimlikleri eşleştirerek çakışma kontrolünü kolaylaştırır.

PendingOperation Tablosu ile İş Kuyruğu

Neden böyle yapılmalı? Her bir CRUD işlemi ayrı bir satırda saklanarak, sıralı ve güvenilir bir yeniden deneme mekanizması sağlanır. İşlem tipi (INSERT, UPDATE, DELETE) ve ilgili veri payload’ı burada tutulur.

Hangi hatalar yapılıyor? İş kuyruğunu sadece bir JSON stringiyle tutmak, veriyi parse ederken performans kaybına ve hatalı veri tiplerine sebep olur.

Pratik adımlarla nasıl uygulanır?

@Entity(tableName = "pending_operations")
public class PendingOperation {
    @PrimaryKey(autoGenerate = true)
    public long id;
    public String operationType; // INSERT, UPDATE, DELETE
    public String entityTable;   // örn: "tasks"
    public String payloadJson;  // Gson ile serialize edilmiş veri
    public long createdAt;
}

Bu tabloyu WorkManager ile periyodik olarak tarayarak senkronizasyonu başlatabilirsiniz.

WorkManager ile Arka Plan Senkronizasyonu

Network Constraints ve Tekrar Deneme Stratejisi

Neden böyle yapılmalı? WorkManager, Android’in enerji yönetimi politikalarına uygun olarak çalışır ve ağ koşullarını göz önünde bulundurur. Böylece cihaz bataryasını tüketmeden senkronizasyon gerçekleşir.

Hangi hatalar yapılıyor? NetworkType.CONNECTED yerine NetworkType.UNMETERED seçmek, Wi‑Fi dışındaki mobil veri kullanıcılarını dışlayabilir. Ayrıca, sınırsız tekrar deneme politikası sunucuya aşırı istek gönderebilir.

Pratik adımlarla nasıl uygulanır?

Constraints constraints = new Constraints.Builder()
        .setRequiredNetworkType(NetworkType.CONNECTED)
        .setRequiresBatteryNotLow(true)
        .build();

OneTimeWorkRequest syncWork = new OneTimeWorkRequest.Builder(SyncWorker.class)
        .setConstraints(constraints)
        .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 30, TimeUnit.SECONDS)
        .build();
WorkManager.getInstance(context).enqueueUniqueWork(
        "offline_sync", ExistingWorkPolicy.KEEP, syncWork);

Bu kod, ağ mevcut olduğunda SyncWorker‘ı çalıştırır ve başarısız olursa üstel gecikmeyle yeniden dener.

Çakışma Çözümü ve Veri Birleştirme

Neden böyle yapılmalı? Aynı kaynağa hem yerel hem de uzaktan aynı anda güncelleme yapılabilir. Çakışma çözümü, veri kaybını önler ve kullanıcıya tutarlı bir görünüm sunar.

Hangi hatalar yapılıyor? “Last write wins” yaklaşımını körü körüne uygulamak, kritik alanların üzerine yazılmasına neden olur. Çakışma çözümünde alan bazlı strateji kullanılmazsa iş mantığı bozulur.

Pratik adımlarla nasıl uygulanır?

  • Sunucudan gelen updatedAt zaman damgasını kontrol et.
  • Yerel kayıt updatedAt daha yeni ise, sunucuya bir UPDATE gönder.
  • Sunucu daha yeni ise, yerel veriyi sunucudan gelen veriyle güncelle.

Bu mantığı bir ConflictResolver sınıfına taşıyarak kod tekrarını önleyebilirsiniz.

Performans ve Ölçeklenebilirlik İçin İpuçları

Batch İşlemler ve API Tasarımı

Neden böyle yapılmalı? Tek tek istek göndermek, ağ gecikmesini artırır ve sunucu üzerindeki yükü gereksiz yere yükseltir. Batch (toplu) endpoint’ler, birden fazla işlemi tek bir HTTP çağrısında göndererek verimliliği artırır.

Hangi hatalar yapılıyor? Batch API’yi tasarlarken çok büyük payload’lar gönderilir ve sunucu timeout hataları alır. Ayrıca, yanıt içinde hangi işlemin başarısız olduğunu belirten detay eksik bırakılır.

Pratik adımlarla nasıl uygulanır?

Adım Açıklama Örnek Payload
1 PendingOperation’ları grupla {“operations”:[{“type”:”INSERT”,”entity”:”tasks”,”data”:{…}}]}
2 Sunucu tarafında toplu işleme {“results”:[{“id”:1,”status”:”success”},{“id”:2,”status”:”conflict”}]}
3 Yerel kuyruktan başarılı satırları sil N/A

Bu yapı, başarısız öğeleri yeniden kuyruğa ekleyerek güvenilirliği sağlar.

Veri Sıkıştırma ve Güvenlik

Neden böyle yapılmalı? Mobil ağlarda veri maliyeti yüksek olabilir. JSON payload’larını gzip ile sıkıştırmak bant genişliğini %70’e kadar azaltır. Aynı zamanda, HTTPS üzerinden şifreli iletişim veri bütünlüğünü korur.

Hangi hatalar yapılıyor? Sıkıştırma uygulanmadan önce payload’ı loglamak, hassas bilgilerin sızmasına yol açar. Ayrıca, sunucu tarafında sıkıştırma kabul edilmediğinde istek 415 hatası döner.

Pratik adımlarla nasıl uygulanır?

OkHttpClient client = new OkHttpClient.Builder()
        .addInterceptor(chain -> {
            Request original = chain.request();
            Request compressed = original.newBuilder()
                .header("Content-Encoding", "gzip")
                .method(original.method(), gzip(original.body()))
                .build();
            return chain.proceed(compressed);
        })
        .build();

Bu interceptor, tüm POST/PUT isteklerini otomatik olarak sıkıştırır.

Test ve İzleme

Neden böyle yapılmalı? Offline senkronizasyonun doğru çalıştığını garanti altına almak için birim testleri ve gerçek cihaz izlemeleri şarttır. Hatalı senkronizasyon veri kaybına yol açabilir.

Hangi hatalar yapılıyor? Yalnızca UI testleri yapılır, arka plan iş akışları test edilmez. Bu yüzden WorkManager görevleri başarısız olduğunda fark edilmez.

Pratik adımlarla nasıl uygulanır?

  • JUnit + Robolectric ile SyncWorker‘ın doWork() metodunu mock network ile test et.
  • Firebase Crashlytics veya Sentry ile senkronizasyon hatalarını raporla.
  • Android Profiler’da CPU ve Network sekmelerini izleyerek batch isteklerin süresini ölç.

Bu adımlar, prod ortamında olası sorunları önceden tespit etmeyi sağlar.

Sıkça Sorulan Sorular

1. Offline senkronizasyon sırasında aynı kayıt iki kez gönderilir mi?

Evet, eğer PendingOperation tablosundan satır silinmezse tekrar kuyruğa eklenir. Bu yüzden SyncWorker içinde başarılı yanıt alındığında ilgili satırı kesinlikle DELETE FROM pending_operations WHERE id = ? ile silmek gerekir.

2. Çakışma çözümünde hangi strateji en güvenli?

Alan bazlı “last write wins” yerine, kritik alanlar için “server wins” ve kullanıcı tarafından değiştirilen alanlar için “client wins” kombinasyonu kullanmak en dengeli yaklaşımdır. ConflictResolver sınıfı bu mantığı merkezi olarak yönetmelidir.

3. Batch API kullanmadan veri kaybı yaşanır mı?

Batch kullanmak zorunlu değildir, ancak yüksek veri hacmi ve sık kesintili bağlantılar için önerilir. Batch olmadan da her bir işlem ayrı ayrı gönderilebilir; sadece performans ve ağ tüketimi artar.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Başa dön tuşu