Erhan Ballıeker

Resilient Network Services – Bölüm 5 – Akavache – 2

Selamlar,

Bir önceki yazımda akavache den bahsetmiş, ne olduğundan, ne faydası olduğundan ve bazı özelliklerinden bahsetmiştim. Bu yazımda bunun devamı olarak ne şekilde kullanabileceğimize, yardımcı metodlarına değineceğim.

Tüm Akavache BlobCache leri aşağıdaki temel metodları destekliyor.

/*
 * Store dan dataları almak..
 */

// tek bir data almak için.
IObservable<byte[]> Get(string key);

// dataların bir koleksiyonunu almak için.
IObservable<IDictionary<string, byte[]>> Get(IEnumerable<string> keys);

// eklediğiniz tipten Deserialize edilmiş tek bir datayı almak için.
IObservable<T> GetObject<T>(string key);

// aynı tipteki Deserialize edilmiş dataların bir koleksiyonunu almak için
IObservable<IEnumerable<T>> GetAllObjects<T>();

// verilen key listesine karşılık gelen birden çok Deserialize edilmiş datayı almak için.
IObservable<IDictionary<string, T>> GetObjects<T>(IEnumerable<string> keys);

/*
 * Store a kayıt eklemek.
 */

// Tek bir data eklemek için
IObservable<Unit> Insert(string key, byte[] data, DateTimeOffset? absoluteExpiration = null);

// bir kolejsiyon eklemek için
IObservable<Unit> Insert(IDictionary<string, byte[]> keyValuePairs, DateTimeOffset? absoluteExpiration = null);

// Otomatik olarak Serialize edilecek bir kompleks objenizi saklamak için.
IObservable<Unit> InsertObject<T>(string key, T value, DateTimeOffset? absoluteExpiration = null);

// Aynı tipteki kompleks objelerinizi saklamanız için.
IObservable<Unit> InsertObjects<T>(IDictionary<string, T> keyValuePairs, DateTimeOffset? absoluteExpiration = null);

/*
 * Store data silmek için.
 */

// tek bir data silmek için
IObservable<Unit> Invalidate(string key);

// belli bir liste silmek için
IObservable<Unit> Invalidate(IEnumerable<string> keys);

// Eğer InserObject ile insert ettiğiniz datalar var ise, aynı şekilde InvalideObject metodu ile silmeniz gerekiyor. 
IObservable<Unit> InvalidateObject<T>(string key);

// aynı şekilde InsertObject edilmiş dataların silinmesi için, tek farkı birden çok datayı silmeniz için bu metod
IObservable<Unit> InvalidateObjects<T>(IEnumerable<string> keys);

// Object olarak kayıt edilmiş olup olmamasına bakılmaksızın tüm dataların silinmesi için
IObservable<Unit> InvalidateAll();

// Belli bir T tipindeki dataların silinmesi için.
IObservable<Unit> InvalidateAllObjects<T>();

/*
 * Saklanan datalar ile ilgili metadata bilgilerini almak için
 */

// eklenen tüm keyleri dönen metod.
IObservable<IEnumerable<string>> GetAllKeys();

// bu key ile saklanmış datanın eklenme tarihini dönen metod.
IObservable<DateTimeOffset?> GetCreatedAt(string key);

// T tipindeki bir objenin eklendiği tarihi dönen metod.
IObservable<DateTimeOffset?> GetObjectCreatedAt<T>(string key);

// verilen key lerin oluşturulma tarihlerinin listesini dönen metod.
IObservable<IDictionary<string, DateTimeOffset?>> GetCreatedAt(IEnumerable<string> keys);

/*
 * Utility metodları
 */

// tüm tamamlanmamış operasyonların diske yazıldığından emin olmak için.
IObservable<Unit> Flush();

// tüm expire olmuş key ve value larını db den silmek için.
IObservable<Unit> Vacuum();

Yukarıdaki tüm metodlar tüm BLobCache tipleri için kullanılabilir. Ama bunlardan daha da kolay olarak işleri çözmemizi sağlayan extension metodlar muhtemelen daha çok tercih edicekleriniz arasında olacaklar.

Bu metodlar aşağıdaki gibi.

/*
 * Username / Login Metodları (Sadece ISecureBlobCache için)
 */

// Verilen host için login bilgilerini saklamak.
IObservable<Unit> SaveLogin(string user, string password, string host = "default", DateTimeOffset? absoluteExpiration = null);

// verilen host taki login bilgilerini okumak.
IObservable<LoginInfo> GetLoginAsync(string host = "default");

// verilen host taki bilgileri uçurmak
IObservable<Unit> EraseLogin(string host = "default");

/*
 * url ve imajların download edilip cachelenmesi
 */

// byte arra olarak download etmek
IObservable<byte[]> DownloadUrl(string url,
    IDictionary<string, string> headers = null,
    bool fetchAlways = false,
    DateTimeOffset? absoluteExpiration = null);

// download edilen datayı imaj olarak okumak.
IObservable<IBitmap> LoadImage(string key, float? desiredWidth = null, float? desiredHeight = null);

// Imajı download edip sonrasında imaj olarak geriye dönmek için.
IObservable<IBitmap> LoadImageFromUrl(string url,
    bool fetchAlways = false,
    float? desiredWidth = null,
    float? desiredHeight = null,
    DateTimeOffset? absoluteExpiration = null);

/*
 * Birleşik işlemler
 */

//Cache deki datayı okumaya çalışır, eğer key i bulamaz ise veya okurken hata
//alırsa verdiğini func ı çalıştırır aldığı sonucu aynı key ile cache e atar.
IObservable<T> GetOrFetchObject<T>(string key, Func<Task<T>> fetchFunc, DateTimeOffset? absoluteExpiration = null);

// GetOrFetchObject gibi sadece async değil.
IObservable<T> GetOrCreateObject<T>(string key, Func<T> fetchFunc, DateTimeOffset? absoluteExpiration = null);

//GetOrFetchObject gibi ama fark olarak verilen key i db de bulsa bile, o sonucu
//hemen döner fakat yine de verdiğiniz func ı çalıştırıp cache deki datayı update eder.
IObservable<T> GetAndFetchLatest<T>(this IBlobCache This,
    string key,
    Func<IObservable<T>> fetchFunc,
    Func<DateTimeOffset, bool> fetchPredicate = null,
    DateTimeOffset? absoluteExpiration = null,
    bool shouldInvalidateOnError = false,
    Func<T, bool> cacheValidationPredicate = null)

Bu iki yazımıda okumuş olsanız dahi akavache github reposunu incelemenizi ve Paul Betts in xamarin evolve 2016 da yaptığı konuşmayı izlemenizi öneririm.

Bir sonraki yazımda görüşmek üzere.

 

Resilient Network Services – Bölüm 5 – Akavache

Selamlar,

Bu yazımda sizlere bu seri kapsamındaki en değerli kütüphanelerden biri olan Akavache den bahsedeceğim.

Akavache async çalışan ve key-value şeklinde data saklamanızı sağlayan, SQLite3 kullanan ve dataları persistant(kalıcı) şekilde tutan bir kütüphane. Desktop ve mobil uygulamalar için eğer lokal veritabanı kullanmak durumunuz var ise biçilmiş kaftan diyebilirim.

Günümüz de neredeyse local storage kullanmayan bir uygulama mevcut değil. Eğer saklamanız gereken dataları da file olarak saklamak gibi bir zorunluluğunuz yok ise kullanacağınız çözüm SQLite olacaktır. Bazı firmalar SQLite üzerinde çalışacak kendi katmanlarını zaten yazmış yada farklı yardımcı kütüphaneler ile ilerliyorlar. Fakat SQLite üzerinde sorgu çalıştırmaki, bilmeden çok fazla hata ve eksik yapmanıza müsait bir durum. En basit örneği verecek olursak, sqlite tarafına attığınız bir sorgu eğer transaction içine alınmadı ise o bunu kendi yapmaya çalışacaktır, bu da sizin sorgunuzun performans kaybetmesi demektir.

Peki hemen öncelikle bu kütüphaneyi nerelerde kullanabiliriz buna bakalım.

Aşağıdaki tüm dotnet platformlarında bu kütüphaneyi kullanabilirsiniz:

  • Xamarin.iOS / Xamarin.Mac
  • Xamarin.Android
  • .NET 4.5 Desktop (WPF)
  • Windows Phone 8.1 Universal Apps
  • Windows 10 (Universal Windows Platform)
  • Tizen 4.0

 

Akavache yi kullandıktan sonra, zaten hali hazırda kendileri de SQLite kullanmalarına rağmen uygulama performanslarında artış olduğunu söyleyen çok firma var. Peki herkes SQLite kullanmasına rağme bu fark neden oluşuyor?

Sebebi şu; SQLite ın nasıl kullanılmaması gerektiğini bilen kişiler tarafından geliştirildi bu kütüphane, ve yılların birikimi open-source bir proje olarak karşımıza çıktı. Yani eğer “Bende yıllardır kullanıyorum ve hali hazırda yazdığım çok iyi çalışan bir kütüphanem var.” diyor olsanız bile yine de kıyaslama yapmanızı öneririm.

Akavache hem gizlilik değeri olan dataları hemde komplex objeleri (imaj, api response, json data) kolayca cihazda saklamınızı sağlıyor.

Temelinde bir core bir key-value byte array store olarak yazılmış (Dictionary<string, byte[]> gibi düşünebiliriz.) ve bunun üzerine inşa ettikleri çok yardımcı ve kullanımı kolay extension metodları mevcut.

Kullanımına bir bakalım.

Akavache öncelikle BlobCache  denen özel bir sınıf ile kullanılıyor diyebiliriz.uygulamanın startup tarafında sadece uyulamanın adını set edeceğiniz tek satır kod ile başlamaya hazır oluyorsunuz.

BlobCache.ApplicationName = “MyApp” veya

Akavache.Registrations.Start(“MyApp”) ve artık hazırsınız.

Cİhazda verilerinizi saklamanız için 4 farklı seçenek mevcut akavache kullanırken.

  • BlobCache.LocalMachine – Normal cache lenmiş data. Bu işletim sistemi farklarına göre sizin haberiniz olmadan, işletim sistemi tarafından uçurulmuş olabiliyor. Bunun garantisini vermiyor, veremezde zaten.
  • BlobCache.UserAccount – User bilgileri. Bazı sistemler bu datayı otomatik olarak cloud ortamlarına yedekleyebiliyor.
  • BlobCache.Secure – password, iban vb hassas dataları saklamanız için kullanmanız gereken seçenek bu.
  • BlobCache.InMemory – Adında da anlaşılacağı üzere sadece uygulamanın lifetime ı süresince datayı saklamak için kullanacağınız seçenek de bu olucaktır.

Xamarin.iOSBlobCache.LocalMachine  seçeneği ile saklanmış dataları, diskte yer boşaltmak için silebilir (tabii uygulamanız çalışmıyor ise o an). Ama  UserAccount ve Secure seçenekleri ile sakladığınız datalar iCloud ve iTunes da yedeklenecektir.

Xamarin.Android  de aynı şekilde LocalMachine üzerinde saklanmış dataları yine diski boşaltmak amaçlı uçurabilir.

Windows 10 (UWP) nin yaptığı bir güzellik ise şu; UserAccount ve Secure tarafında saklanmış dataları cloud a atıyor ve tüm yüklü cihazlar ile senkronize ediyor.

Akavache yi async kullanmak için System.Reactive.Linq api si önemli. async-await bu şekilde çalışır oluyor.

using System.Reactive.Linq;    

Akavache.Registrations.Start("AkavacheExperiment")

var myToaster = new Toaster();
await BlobCache.UserAccount.InsertObject("toaster", myToaster);


var toaster = await BlobCache.UserAccount.GetObject<Toaster>("toaster");


Toaster toaster;
BlobCache.UserAccount.GetObject<Toaster>("toaster")
    .Subscribe(x => toaster = x, ex => Console.WriteLine("No Key!"));

Yukarıda görüceğiniz üzere, using olarak System.Reactive.Linq sayesinde aşağıda UserAccount üzerinden GetObject yaparken  await işlemi yapabiliyoruz.

Hepsinden önce ilk olarak yukarıda da belirttiğim gibi uygulama adını register ettikten sonra akavache kullanıma hazır hale geliyor.

Async-Await ile kullanmak istemiyorsak, aşağıda yazdığımı gibi Subscribe metodu ile cache aldığımız data üzerinden işlemimizi yapabiliyoruz.

Xamarin Linker Önlemi

Bazılarınız uygulama boyutunu düşürmek için Xamarin Linker ı kullanmıştır. Proje taranıp kullanılmadığı düşünülen dll ler uçurulabilir.

Akavache.Sqlite3 dll inin xamarin build tooları ile projeden uçurulmasını önlemenin(ki bu çok öenmli =) iki yolu var

1). İlk yönetim aşağıdaki gibi, dll deki type ları referance alıcak bir dosya eklemek projeye.

public static class LinkerPreserve
{
  static LinkerPreserve()
  {
    var persistentName = typeof(SQLitePersistentBlobCache).FullName;
    var encryptedName = typeof(SQLiteEncryptedBlobCache).FullName;
  }
}

2) İkinci yöntem ise, ilk belirttiğim gibi;

Sadece uygulamanın adını Akavahce.Registrations.Start metodu ile vermek.

Akavache.Registrations.Start("ApplicationName")

ShutDown

Akavacheyi kullanırken unutmamanız gereken birşey de uygulamanın shut down olayında BlobCache.ShutDown() metodunu çağırmamız. Hatta .Wait() etmemiz. Bunu yapmaz isek, queue ya alınmış datalarımız, önbellekten uçmadan kalabilir.

Bu kadar Akavache dn bahsettikten sonra bir sonraki yazımızda daha detaylı kullanımından ve extension metodlarından bahsedeceğim.

Görüşmek üzere.

Resilient Network Services – Bölüm 4 – ModernHttpClient

Selamlar,

Bu yazımda sizelere ModernHttpClient kütüphanesinden ve uygulamanıza performans olarak katkılarından bahsedeceğim.

ModernHttpClient kullanması belki de en basit kütüphane olabilir 🙂 ama etkileri fazlaca.

Bir xamarin uygulamanızda HttpClient ile beraber yapacağınız requestleri u kütüphane ile yaptığınızda çok hızlı çalıştığını göreceksiniz. Bunun sebebi ise bu kütüphanenin arkada platform spesifik taraflarda kullandığı native kütüphaneler. Bu kütüphaneler şunlar

Native development yapmış olanlarınız ya bu kütğphaneleri doğrudan kullanmıştır ya da bu kütüphaneleri arkaplanda kullanan başka kütüphaneler kullanmıştır.(AFNetwork gibi)

Bu iki kütüphane hakkında hiçbir fikriniz olmasa bile uygulamanızda bu ikisini kullanıp daha hızlı bir API haberleşmesi için yapmanız gereken sadece şu;

var httpClient = new HttpClient(new NativeMessageHandler());

Xamarin projenizde sadece Portable yada standard projenize ModernHttpClient ı referans ederek bu şekilde kullanıma başlayabilirsiniz.

Kütüphanenin kullanımı kadar kısa ama kendisi kadar faydalı bir yazı olduğunu umarım 🙂

Bir sonraki yazımda görüşmek üzere.

Resilient Network Services Bölüm 3 – Fusillade

Selamlar,

Önceki yazılarımda Resilient Network services kurgusundan ve bu kurguyu yaparken bize çok destek olacak olan kütüphanelerden bahsetmiştim.  En son REFIT ten bahsetmiştik, şimdi sıra geldi Fusillade den bahsetmeye.

Bu da yine mobil ve desktop uygulamalırınızda daha efektif işler yapabilmenizi sağlayan, Volley ve Picasso kütüphanelerinden esinlenerek oluşturulmuş bir C# kütüphanesi. Volley başarılı bir Http haberleşme kütüphanesi android tarafında. Picasso ise Android developer ların olmaz ise olmalardından bir tanesi. Peki bakalım bu Fusillade de bunlardan esinlenerek neler  yapılmış.

Öncelikle hangi ortamlarda bu kütüphaneyi kullanabileceğimizi görelim.

  • Xamarin.Android
  • Xamarin.iOS
  • Xamarin.Mac
  • Windows Desktop apps
  • WinRT / Windows Phone 8.1 apps
  • Windows Phone 8

Bu ortamların hepsinde bu kütüphaneyi kullanmamız mümkün. Kendisi portable bir kütüphane, NetStandard kütüphanesi kullanmaya başlayanlarda rahatlıkla kullanabilirler.

Peki bu kütüphane bize neler sağlar bunlara bir bakalım. Fusillade temelde HttpMessageHandler kümesi diyebiliriz. HttpClient ile yaptığımız requestlerde arka tarafta bu kullanıldığından, Fusillade kullanarak yaptığınız reuestlerin daha iyi test edilmiş senaryolar üzerine kurulu HttpMessageHandler ile çalışmasından dolayı daha verimli ve responsive uygulamalar yazmanızda yardımcı olur. Özetle Fusillade bize 4 temel fayda da bulunuyor.

  • Auto-deduplication of relevant request: Türkçe mealine gelecek olursak bu deduplication özelliğinin bize sağladığı şey şu; örneğin bir mobil uygulamanız var, sosyal medya olsun. Kullanıcıların postlarını aşağıya doğru diziyorsunuz, bir arkadaşınız 3-5 post atmış. Bunları gösterirken yanında profil resmini de göstereceksiniz değil mi? E bu resimde çok büyük olasılıkla uygulamaya gömülü olmayacağına göre network üzerinden istek atıp alacağınız bir resim olucaktır. İşte bu ve benzeri durumlarda, Fusillade birbirine benzer requestler için yalnızca tek bir request atıyor ve dönen result ı tüm instance lar ile paylaşıyor. Bu da bize daha az network trafiği oluşturucağımız için performans artısı olarak geri dönüyor.
  • Request Limiting : Bu özellik bize Fusillade tarafından anlık yapılan istekleri kısıtlama imkanı sağlıyor. Instagram’ı düşünelim, like butonuna sürekli basıp duruyoruz, her seferinde request atmak yerine bu gibi davranışları kısıtlamak istediğimiz durumlarda bu özelliğini kullanabiliriz. Yapılan istekler, Volley default u olarak anlık 4 tane olacak şekilde limitleniyor. Bunun la istediğiniz gibi oynayabilirsiniz tabii ki.
  • Request Prioritization: Bu özellik aslında en çok kullandığım Fusillade özelliklerinden biri. Uygulamanızda bir ekranda bir çok widget olduğunu aynı zamanda bunlar load olurken de uygulama responsive olduğu için kullanıcının aynı zamanda birşeylere basıp kendi birşeyleri tetiklediğini düşünelim. İşte bu gibi durumlarda Fusillade hangi requestleri önceliklendirip hangilerini geri planda bırakabileceğini söyleyebiliyorsunuz.
  • Speculative requests: Bu da yine özellikle mobil uygulamalrda kullanabileceğiniz güzel bir özellik. Örneğin ilan listesi açtınız uygulamanızda, en üstte de 5 adet reklam var, tıklanma olasılığı yüksek ilanlar. İşte Fusillade ye şunu söyleyebiliyorsunuz; 5mb data sınırın var, bu 5 adet reklamın requestlerini speculative olarak yolla, 5mb bulmadığın sürece bunların detaylarını al getir, 3. de 5mb doldu mu, o zaman gerisini getirme. Bu kullanıcıların tıklama olasılıklarını yüksek gördüğünüz senaryolarınızda requestleri önceden belli bir limit verip yollamınıza ve kullanıcı tıkladığında hiç bekletmeden doğruca ilgili cevabı göstermenize yarıyan bir özellik.

Kullanım Şekli

Fusillade yi kullanmaya başlamanın en kolay yolu NetCache  adındaki sınıf. Yukarıda yazdığım birçok özelliği zaten kapsıyor.

public static class NetCache
{
    public static HttpMessageHandler Speculative { get; set; }
 
    public static HttpMessageHandler Background { get; set; }
    public static HttpMessageHandler UserInitiated { get; set; }
}

Bu sınıfı aşağıdaki şekilde HttpClient ile beraber kullanabilirsiniz.Aşağıda ilgili request e atılacak olan isteğin kullanıcı tarafından tetiklendiğini belirtmiş olduk.

var client = new HttpClient(NetCache.UserInitiated);
var response = await client.GetAsync("https://randomuser.me/api/?results=5");
var str = await response.Content.ReadAsStringAsync();

Console.WriteLine(str);

Speculative requestlerin limiti şu şekilde değiştirebiliriz.

NetCache.Speculative.ResetLimit(1048576 * 5/*MB*/);

Offline Desteği

Cache için ben genelde Akavache kullanıyor olsamda, yada primitive tipler için platform spesific taraflardaki SharedPreference ve NSUSerDefaults u kullanıyor olsam da, Fusillade bize responseları cache leme ve offline olarak uygulamayı çalıştırma fırsatı da veriyor. Cache için olan objenizi NetCahce in RequestCache Propertysine set ettiğiniz anda gelen responselar burada cachlenir.

NetCache.RequestCache = new MyCache();

var client = new HttpClient(NetCache.UserInitiated);
await client.GetStringAsync("https://httpbin.org/get");

Daha sonra kullanıcının offline olduğu durumda NetCache ile HttpClient a request in  Offline çalışmasını istediğinizi aşağıdaki gibi söyleyebilirsiniz.

var client = new HttpClient(NetCache.Offline);
await client.GetStringAsync("https://httpbin.org/get");

Tüm kütüphane yi github  repo sundan incelemnizi öneririm.

Bir sonraki yazımda görüşmek üzere.