Android WorkManager Kullanımı

Ömer Ateş
6 min readJul 5, 2021

--

Mobil cihazlarda kullanıcıların aktivitelerine karşın birtakım senaryo dizisi gerçekleşmektedir. Örneğin kullanıcı bir butona bastığında sunucuya istek atıp dönen sonucu görene kadar ekranı bloke edebiliriz. Diğer bir senaryo ise kullanıcı bu sefer butona bastığında bir sunucudan bir dosya indirilmesi işlemi gerçekleştirdiğini farz edelim. Burada kullanıcı uygulamadan çıktığında indirme işleminin devam etmesini isteriz. Bu tip durumlarda hangi senaryonun foreground ya da background da olacağını iyi belirlememiz gerekir.

Background da geliştireceğimiz örnek bazı durumlar vardır. Kullanıcıdan bağımsız uzak sunucuya bir yedekleme yapılması gerektiğinde, bir görsele uzun sürecek filtreleme uygulandığında, backup alma vb. durumlarda kullanabileceğimiz modern teknolojilerden WorkManager bizleri karşılıyor.

WorkManager, kullanıcı ile etkileşim halinde olmayan işlemlerde background da çalıştırılan asenkron görevler tanımlayabileceğimiz bir yapıdır. Google, cihaz background unda çalışan işlemlere çok sıcak bakmamaktadır. Bunun en önemli sebeplerinden biri, cihazların bataryasını aşırı tüketmeleridir. WorkManager bu durum karşısında batarya dostu diyebiliriz çünkü arka planda gerçekleştireceğimiz görevlerde cihazın güç tasarrufu moduna ya da anlık özelliklerine bağlı olarak başlatabiliriz.

Peki Başka Ne Özellikleri var ❓

  • Görevleri Anlık Cihaz Durumuna Göre Çalıştırma: Kullanıcı cihazını olumsuz etkilememek için WorkManager bizlere bazı kısıtlanmış özelliklerden faydalanmamızı sağlar. Örneğin; sadece WI-FI açık olduğunda, cihaz bataryasının sağlıklı olduğu durumlarda veya cihaz depolama alanı yeterli olduğunda background görevlerini tetikleyebiliriz.
  • Birden Fazla Görevi Kolayca Yönetebilme: Art arda birden fazla görev oluşturabilir ve kolayca yönetebiliriz. Görevlerin paralel veya sırasıyla gerçekleşmesini belirtebiliriz. Bu sırada görevlere belli veriler gönderip diğer görevler için bir çıktı alabiliriz. Ayrıca tanımladığımız görevleri ileride bahsedeceğim tag ve name vererek sonradan iptal edebiliriz.
  • Yeniden Deneme Politikası: Başlattığımız görevler çalışma sırasında bazı aksaklıklar sonucunda başarısız olurlarsa tekrar aktif hale getirilebilirler.

Not: Projeniz Android 10 (API 29) ve üzerini destekleyecek ise background görevleri için önceden kullanılan FirebaseJobDispatcher ve GcmNetworkManager kullanmanız crash almanıza neden olacaktır. Bu durumda WorkManager’a geçmeniz zorunlu hale gelecektir 😉

WorkManager’ı projeye module seviyesinde aşağıdaki gibi ekleyelim:

implementation "androidx.work:work-runtime-ktx:2.1.5"

Basit Bir Görev Tanımı

Bir görevin tanımlanması için Worker sınıfından miras almamız gerekmektedir. Ardından doWork() fonksiyonunu override etmeliyiz. Yapılacak tüm geliştirmeler bu fonksiyon içerisinde gerçekleşmelidir. Buradaki örnekte verilerin yedeklenmesi için bir yapı oluşturdum.

Yukarıda bir işin sonuçlarına göre bazı değerler gönderebiliriz. Örneğin Result.success() ile işin başarıyla bittiğini Result.Fail() işin herhangi bir nedenden dolayı başarısız olduğunu ya da Result.retry() ile işin başka bir zamanda tekrar gerçekleşmesi için sonuçlar dönebiliriz.

Not: Bir iş için miras aldığımız Worker sınıfı haricinde coroutine ler ile çalışmak isteyebiliriz. Bu durumda CoroutineWorker sınıfını miras alabiliriz ya da RxJava kullanmak istersek de RxWorker sınıfını kullanabiliriz. Bu sınıfları daha detaylı incelemek için buradan dökümana gidebilirsiniz.

WorkManager, bizlere iki tür iş tanımı sunar. Belli aralıklarla çalışan işler için PeriodicWorkRequestBuilder ; Sadece bir kere çalışacak işler için ise OneTimeWorkRequestBuilder kullanılır.

Periyodik yapmak isteğimiz işleri kendi içlerinde de flexTimeInterval ile aralık belirtebiliriz.

Yukarıda belirtiğim gibi bir işi kendi periyodu içerisinde çalışma zamanını ayarlamak mümkündür. Örneğimizde 1 saat aralıklarla çalışmasını istediğimiz bir iş mevcuttur. Her 1 saatlik periyodun son 15 dakikalık aralıklarında herhangi bir anda işi başlatabiliriz.

Not: Şunu unutmamalıyız ki herhangi bir iş için belirlediğimiz süre cihazın durumuna bağlıdır. Örneğin her sabah 06:00'da bir yedek alma işi yaptığımızda bu süre 05:50 ya da 06:10 aralıklarda olabilir lakin Android sisteminin er ya da geç çalıştırmak istediğimiz işi kesin çalıştıracağının sözünü vermesi ayrı güzel bir yanıdır 😍

Belirlediğimiz Cihaz Koşullarında İşlerimizi Gerçekleştirme

WorkManager’ın en güzel özelliklerinden biri de Constraints dir. Aslında bu özellik sayesinde işlerimizi bilinçli ve daha güvenli adımlarla çalıştırma imkanı sunulur.

  • NetworkType: Yedekleme işi yaparken sadece WI-FI kısıtlamasını burada yapabiliriz.
  • BatteryNotLow: Cihaz bataryasını göz önünde bulundurduğumuzda bu özelliği true olarak ayarlarsak sadece yeterli batarya ömrü olduğu durumda çalışacaktır.
  • RequiresCharging: Uzun sürecek bir işlem için cihazın performansı ya da pil tüketimi aşırı düşebilir. Bu durumlarda ise cihaz sadece şarja takılıyken işlerimizi başlatabiliriz.
  • DeviceIdle: Bu özelliği true yaptığımız takdirde sadece cihaz boşta iken işlerimiz çalışacaktır.
  • StorageNotLow: Uzak sunucuya yedeklemenin tam tersi durumu düşündüğümüzde belli aralıklarda cihaza bir veriler kümesi yollayalım. Bu durumda cihazda yer olup olmamasına dikkat etmememiz gerekir. Bu özelliği de true olarak ayarlarsak bu durumu da handle etmiş oluruz.

Yukarıdaki örnekte ise Constraints.Builder() ile sadece WI-FI ağ türüne bağlı ve Şarj edildiği zamanda çalışacak bir iş planı oluşturduk.

Tekrarlanabilir İşler Oluşturma

Var olan bir iş herhangi bir şekilde başarısız olursa Result.retry() ile tekrar tekrar işi başlatabiliriz. Bu gibi durumda bize tanınan iki tip özellik vardır:

  • LINEAR: X saniye sonra iş isteğini tekrar başlatmak için.
  • EXPONENTIAL: X Saniye sonra iş isteğini tekrar başlat. Eğer yine başarısız olursa X+X süre sonra tekrar iş isteğini başlatabiliriz. Yani istek süresi her başarısız istek de katlanarak tekrar başlatılmaktadır.

Yukarıdaki örnekte ise sadece bir kere çalışmasını isteğimiz bir iş tanımı yaptım ve işin başarısız olma durumunda 10 saniye sabit aralıkla tekrar iş isteği atılmasını belirttim.

TAG - Benzersiz İşler Oluşturma

Her workRequest in bir tag ı olmalıdır çünkü ilgili işi gözlemlemek ya da iptal etmek isteyebiliriz. Belirlediğimiz bu tag unique olmalıdır.

Birden fazla işin olduğu durumlarda isteğimiz işintag bilgisi ile ulaşmak isteyebiliriz. Bu durumda WorkManager.getInfosByTag(verilen tag) fonksiyonu kullanabiliriz. Ayrıca oluşturulmuş tüm işlerin tag lerine ulaşmak için ise WorkManager.getTags() fonksiyonunu kullanabiliriz.

Worker içerisine Veri Gönderme

Yapılacak işlerde kullanmak üzere veri gönderimi sağlayabiliriz. Örneğin bir request atılacağı zaman bir path eklenebilir ya da bir dosyayı kaydetmek için url gönderebiliriz. Gönderilecek veriler key-value şeklinde gönderilmektedir.

Yukarıdaki örnekte bir image url gönderdik. Ayrıca basit bir json verisi göndermek isterseniz Gson kütüphanesini kullanarak json ı string tipine dönüştürdükten sonra kullanabilirsiniz.

Bu İşler Nasıl Çalışır ❓

Her işin belli durumları vardır. Bu durumlar tek seferlik ve periyodik işlerin çalışma durumlarında farklılık göstermektedir.

Tek Seferlik İşlerin Çalışma durumları

One Time Work Request States

Bir işi oluşturduktan sonra WorkManager’a enqueue fonksiyonuna gönderdikten sonra süreç başlamaktadır. Eğer bir gecikme durumu ve belirlediğimiz kısıtlamalar var ise bunların gerçekleşmesi halinde Enqueued durumuna geçiş sağlanır. Ardından Running durumuna geçerek worker içerisinde gerçekleşmesini isteğimiz işler gerçekleşir. İşin sonucuna göre ise Successed , Failed ya da Cancelled durumları ile yapılacak iş sonlandırılır. Herhangi bir nedenden dolayı Enqueued aşamasında bir sorun oluştuğunda ise direkt Cancelled durumuna geçiş sağlanmaktadır. Bir işin bittiğini kontrol etmek isteyebiliriz. Bu durumda WorkInfo.State.isFinished() fonksiyonunu çağırabilirsiniz.

Periyodik İşlerin Çalışma Durumları

Periodic Work Request States

Tek seferlik işlerde olan Successed ve Failure durumları Periyodik işlerde yoktur. Periyodik işler deki fark şudur: Bir işin başarılı veya başarısız olma durumunda iş tekrardan başlamaktadır. Sadece işi Cancelled durumuna getirdiğimizde iş iptal olmaktadır.

“Name” Kullanımı

Bazı durumlarda aynı işin tekrar tekrar kuyruğa eklenmesi istenmez bir durum olabilir. Bu durumda işi name ile etiketleyebiliriz. Tek seferlik işler için enqueueUniqueWork(); Periyodik işler için enqueueUniquePeriodicWork() fonksiyonunu kullanabilirsiniz. Tag lerden farklı olarak name ler her işe özel tanımlanmalıdır. name için verdiğimiz değer otomatik oluşturulmaz. Biz geliştiricilerin belirtmesi gerekir. Sonuç olarak name kullandığımız bir iş isteği çalışırken aynı name i kullanan başka bir iş isteğinin kuyruğa alınması engellenir.

Bir İşin Çalışma Durumlarını İzleme

Bir işin çalışma durumlarını gözlemleyebilir, kullanıcıyı bilgilendirebilir ya da bir aksiyon gerçekleştirebiliriz. Ayrıca LiveData desteğini de burada kullanabiliriz. İşi izlemek için tag, name ya da id ile etiketlenmiş bir iş olabilir.

LiveData desteği ile:

İşler Arası Çakışmalar Nasıl Çözülür?

Aynı anda çalışmak isteyen işler çakışmaya neden olmaktadır. Bu durumu WorkManager’a belli sabitler göndererek bildirebiliriz. Bu sayede işler arası öncelikleri belirler ve akışı daha kolay yönetebiliriz.

Tek seferlik işler için ExistingWorkPolicy den REPLACE KEEP ve APPEND kullanırız; Periyodik İşler için ise ExistingPeriodicWorkPolicy den REPLACE ve KEEP sabitlerini kullanırız.

Aktif çalışan bir iş varsa ve aynı uniquename değerine sahip başka iş ile çakışıyorsa:

REPLACE: Mevcut iş iptal edilip yeni iş devreye alınır.

KEEP: Mevcut işin durumunu korur.

APPEND: Çakışan iş mevcut işin durumunu etkilemez ve alt iş olarak eklenir. Mevcut iş bittiğinde çakışan iş devreye girer. Bu da zincirleme bir yapı kurulduğunu gösterir.

Not: Burada dikkat edilmesi gereken durum şudur: Mevcut çalışan işin sonucu çakışan işin durumunu etkileyebilir. Örneğin mevcut iş başarısız olduğunda zincirin alt işlerine girmez ve kuyrukta diğer işler sonlanır. Bu durumu çözmek için ise APPEND_OR_REPLACE sabitini kullanabiliriz.

Periyodik işler için sadece ExistingPeriodicWorkPolicy den REPLACE ve KEEP sabitlerini kullanırız. Bu sabitlerin anlamları yukarıda açıklanan anlamlarıyla aynıdır.

Mevcut İşin İptal Edilmesi/Durdurulması

Bir işi kuyruktan çıkarabilir ya da iptal edebiliriz. Bu aksiyonlar için yine TAG ID ya da name değerlerini kullanabiliriz.

WorkManager konusunu pekiştirmek için temel bir backup alma senaryosunu gerçekleştirdiğim proje oluşturdum. Proje kodlarına buradan erişebilirsiniz. Umarım faydası olmuştur. Yazının daha da uzamaması için zincirleme işlerdeki durumları ve işleri arkaplan da izlememezi, akışları görmemizi sağlayan Background Inspector eklentisini sonraki yazılarıma saklayacağım. Sağlıcakla kalın 👋

Kaynaklar:

https://developer.android.com/topic/libraries/architecture/workmanager

--

--