Windows Phone'da Background Task'lardan PeriodicTask

0 dakikada yazıldı

34599 defa okundu

Düzenle

Windows Phone'da background'da işlem yaptırmak istediğiniz
seçeneklerinizden biri "PeriodicTask"ler. PeriodicTask'ler
uygulamanızdan bağımsız olarak arka planda belirli aralıklar çalışarak
işlem yapabilen parçacıklar. 1GB ve üstü belleğe sahip Windows
Phone'larda 20MB bellek kullanabilen, daha düşük cihazlarda ise
11MB bellek limiti ile yaşamak zorunda olan PeriodicTask'lerin
implementasyonuna bakmadan önce istersen ne şartlar altında ve nasıl
çalıştıklarına göz atalım. Unutmadan, eğer bellek limitlerini geçerseniz
taskiniz doğrudan sonlandırılır.

Zamanlama herşeydir...

PeriodicTask'ler uygulamanız tarafından ilk başlatıldığı ve "schedule"
edildiği andan itibaren iki hafta süre ile çalışırlar. İki hafta
içerisinde eğer uygulamanız kullanıcı tarafından hiç açılmazsa artık söz
konusu Task de işletim sistemi tarafından çalıştırılmaz. Tabi bilmemiz
gereken bazı istisnai durumlar var. Bunlardan ilki uygulamanız Start
ekranına pinlenmişse ortaya çıkıyor.  Uygulamanız tarafından Tile'a her
update geçildiğinde Task'in da iki haftalık timeout süresi resetleniyor.
İkinci istisnai durum ise uygulamanızın "lock screen"de notification
göstermek üzere ayarlanmış olması. Yine her notification update ile iki
haftalık task timeout süresi resetlenmiş oluyor.

Detaylar bu kadar ile bitmiyor. Eğer PeriodicTask'iniz iki defa üst
üste hata
alarak sonlanırsa işletim sistemi tarafından doğrudan pasif
hale getiriliyor ve tekrar çalıştırılmıyor. Task'in tekrar çalışması
için ana uygulama tarafından tekrar schedule edilmesi şart.

Bir PeriodicTask normal şartlarda ortalam 30 dakikada bir
çalıştırılır
.  Bu 30 dakikalık süre bazen 20 veya 40 dakika da
olabilir. İşletim sistemi genel olarak farklı uygulamalardan gelen tüm
PeriodicTask'leri aynı anda çalıştırmaya çalışır. Çalışma anlarının
senronize edilebilmesi için 30 dakikalık aralıkları işletim sistemi
tarafından modifiye edilebilir. Bu konuda sizin pek yapabileceğiniz bir
şey yok. Bir PeriodicTask çalıştığında işini bitirmesi için 25
saniyesi
var.

İlk PeriodicTask'imiz.

Uygulamanıza bir PeriodicTask eklemek için ilk olarak Windows Phone
projenizin bulunduğu Solution'a yeni bir "Windows Phone Scheduled Task
Agent
" projesi eklemeniz gerekiyor. Bunun için normalde yaptığınız
gibi "Solution Explorer"'a sağ tıklayıp "Add New Project" diyip gelen
listeden proje şablonunu seçenebilirsiniz. Artık Solution içerisinde iki
proje var. Bunlardan ilki Windows Phone projeniz ikincisi ise
PeriodicTask'in bulunduğu ScheduledTaskAgent projesi. Bir sonraki
adımda bu iki projeyi birbiri ile ilişkilendirmek, yani yarattığımız
yeni Agent'ın bizim Windows Phone uygulaması tarafından kullanılacağını
ve yönetileceğini belirtmek için Windows Phone uygulamasının
referanslarına Agent projesini eklememiz gerekiyor. Hemen WP projesinin
References listesine sağ tıklayıp "Add Reference" diyerek "Solution /
Projects" içerisinden Agent projemizi seçiyoruz. Proje ortamını
ayarlamayı bitirdiğimize göre Agent projesinin içindeki
"ScheduledAgent.cs" dosyasına zıplayabiliriz. Bu dosya bizim
PeriodicTask için yazacağımız tüm kodları içerecek olan dosya.

[C#]

protected override void OnInvoke(ScheduledTask task)
{
    Microsoft.Phone.Shell.ShellToast toast = new Microsoft.Phone.Shell.ShellToast();
    toast.Title = "Background Agent Sample";
    toast.Content = "Herhangi bir mesaj içeriği!";
    toast.Show();
    // Debug modundaysak dakikada bir çalıştır
            #if DEBUG_AGENT
              ScheduledActionService.LaunchForTest(task.Name, TimeSpan.FromSeconds(60));
            #endif
    // Agent'ın işinin bittiğini işletim sistemine haber verelim.
    NotifyComplete();
}

Yukarıdaki kod bizim örnek Agent'ımızın kodu olacak. Burada basit bir
şekilde PeriodicTask'a ToastNotification göstertiyoruz. Siz tabi ki
gerçek uygulamalarda daha anlamlı işler yaptıracaksınız :) Önemli olan
bu kod içindeki üç noktayı yakalamak. Birincisi "OnInvoke" metodu.
İşletim sistemi Task'i her başlattığında bu metodu çalıştıracak. Yani
çalıştırmak istediğiniz her şeyi buraya yazmanız gerekiyor. İkinci
önemli nokta ise işiniz bittiğinde kesinlikle "NotifyComplete"'i
çağırmanı gerektiği. Bunu çağırmazsanız işletim sistemi taskin işini
bitirdiğini hiç bir zaman bilemeyecek. Son olarak satır arasında
yakalayabileceğiniz bir diğer detay da sadece Debug modu için araya
sıkıştırdığımız kod. Malum debugging modunda 30 dakika taski bekleyemek
istemezsiniz. Sadece Debug modunda çalışan kodumuzun çalışabilmesi için
dosyanın en üstünde #define** DEBUG_AGENT** tanımlamasını
yapmayı da unutmayın.

Sıra geldi hazırladığımız Agent'ı ana uygulamamızdan Schedule etmeye,
yani planlamaya.

[C#]

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    string TaskAdi = "DenemeTask";
    PeriodicTask periodicTask = ScheduledActionService.Find(TaskAdi) as PeriodicTask;
    //Agent varsa önce bir kaldıralım.
    if (periodicTask != null)
    {
        ScheduledActionService.Remove(TaskAdi);
    }
    //Yeni Task yaratıyoruz.
    periodicTask = new PeriodicTask(TaskAdi);
    //Description vermemiz şart.
    //Bu açıklama Windows Phone Settings sayfalarında gözükecek.
    periodicTask.Description = "Deneme amaçlı bir Task";
    ScheduledActionService.Add(periodicTask);
    // Debug moddaysak bir dakikadan çalıştıralım Task'i.
            #if(DEBUG_AGENT)
    ScheduledActionService.LaunchForTest(TaskAdi, TimeSpan.FromSeconds(60));
            #endif
    base.OnNavigatedTo(e);
}

Yukarıdaki kodu adım adım inceleyelim. Ben kodu doğrudan örnek
uygulamanın ilk açılına koydum. Siz kendinize göre ayarlayabilirsiniz.
Dönelim konumuza. Her Task kendi adı ile işletim sistemine
kaydettiriliyor. Eğer aynı isimde aynı uygulamadan başka bir Task var
ise bu sizin daha önce yarattığınız bir Task olsa da onu güncelleme
şansınız yok. Eski Task'i kaldırıp yenisini tekrar koymanız gerek. Bu
arada unutmadan, her uygulamanın zaten sadece tek bir BackgroundTask'i
olabilir. Kodumuz içerisinde de dikkat ederseniz ilk olarak adı ile
Task'imizi bulmaya çalışıyoruz. Eğer aynı isimde daha önce koyduğumuz
bir Task var ise onu alıp kaldırıyoruz. Sonrasında artık yeni Task'imizi
ekleyebiliriz. Bir diğer ince nokta da her Task'in Description'ının
olması gerektiği. Bu Description'lar doğrudan Windows Phone'un Settings
sayfalarında gösteriliyor.

Task'lerin Description'ları Settings sayfalarında gözüküyor.
Task'lerin Description'ları Settings sayfalarında gözüküyor.

Debug modunda Task'leri test edebilmekle ilgili sorunumuz burada da
kendini gösteriyor ve bu sorunu aşmak için yine aynı taktiğe
başvuruyoruz. Bu sefer LaunchForTest adında bir metoddan
faydalanıyoruz ve bir dakika içerisinde Task'in emülatörde çalışmasını
sağlıyoruz.

Background Agent'dan gelen Toast Notification.
Background Agent'dan gelen Toast Notification.

Örneği bu noktada çalıştırdığınızda herşeyin sağlıklı bir şekilde
çalıştığını göreceksiniz ama özünde dikkat edilmesi gereken birkaç nokta
daha var. Bunlardan ilki kullanıcının kasıtlı olarak Windows Phone'un
settings sayfalarına gidip uygulamanızın Background Agent'ını kapattığı
senaryo. Bu durumda siz istediğiniz kadar Task eklemeye çalışın sürekli
olarak "InvalidOperationException" alırsınız. Bu hatayı alacağınız
bir diğer senaryo da arka planda çalışabilecek Task sayısı limitinin
dolmuş olması. Eğer kullanıcı Windows Phone'un arka planda
çalıştırabileceği maksimum Task sayısına gelmiş ise sizin uygulamanız
bir Task eklemeye kalktığınıda yine "InvalidOperationException"
alacaktır. Peki bu iki durumu nasıl ayırt ederiz?

[C#]

try
{
    ScheduledActionService.Add(periodicTask);
}
catch (InvalidOperationException exception)
{
    if (exception.Message.Contains("BNS Error: The action is disabled"))
    {
        MessageBox.Show("Background Agent'ları bu uygulama için bloklamışsınız.");
    }
    if (exception.Message.Contains("BNS Error: The maximum number of ScheduledActions of this type 
                                                                        have already been added."))
    {
        //Bu noktada bir şey yapmaya gerek yok. İşletim sistemi zaten gerekli uyarıyı gösterecektir.
    }
}     

Yukarıdaki kodda da görebileceğiniz üzere bahsettiğimiz Exception tipini
Task'i eklerken yakalayarak Exception Message'a bakmamız gerekiyor.
Mesajın içeriğine göre kullanıcıya gerekli uyarı göstermek en doğru
yöntem olacaktır.

Sanırım hepsi bu kadar :) Son bir uyarı olarak hatırlatiyim; WP7.1
uygulamalarında emülatör Background Task'leri için bellek ve 25 saniye
çalışma süresini emüle etmez. WP8 uygulamalarında ise bu emülasyonu
kullanabilirsiniz. Eğer WP7.1 uygulaması geliştirecekseniz bu detaya
dikkat etmekte fayda var.

Örnek projenin kodlarını her zamanki gibi
Github'da
bulabilirsiniz.

Kendinize çok iyi bakın. Görüşmek üzere.