Android Servisler

Android Servisler , Arkaplanda çalışan fakat uygulamanın kendisinin aktif olmasını gerektirmeyen yapıların genel isimleridir.Android servisler Windows Servise ve ya Linux Deamonlara benzetilebilmektedir. Android Servisler genel olarak iki gruba ayrılabilir:

  1. Local Servisler
  2. Remote Servisler

Local Servisler , Uygulama içerisinde başlatılan (uygulamaya ait) kendisi ya da başlatılan tarafından sonlandırılmazsa sonlanmayan servislerdir.

Remote Servisler ise başka bir process tarafından başlatılabilen (binding) uygulama bağlantısını kestiği zaman(unbinding) durdurulan servislerdir.

Bir servis local servis ise Context sınıfının startService metodu ile , Remote servis ise başka bir process tarafından bindService isimli metotla başlatılabilir.

Local servislerin başlatılmasını start, başka bir process tarafından servislerin başlatılmasına ise bind terimi kullanılır.

Bir servisin aşağıdaki gibi bir yaşam ömrü bulunmaktadır:

Bu yaşam döngüsü içerisinde eğer bir servis startService metoduyla başlatılıyorsa onStartCommand metodu ,

bindService ile çağrılıyorsa onBind metodu çağrılır.

Bu yaşam döngüsü içerisinde onCreate metodu onStartCommand ve ya onBind metodundan önce çağrılmaktadır.Servis yok edildiğinde her iki durumda da onDestroy metodu çağrılmaktadır.

Servis başlatmak için de tipik olarak Intent sınıfı kullanılmaktadır.

Android Service'ler uygulamanın ana Thread'i içerisinde çalışmaktadırlar.Servis içerisinde yeni bir Thread yaratılacaksa buna dikkat edilmelidir.

Bir Servisin, Service ya da Service sınıfından türemiş başka bir sınıftan türemiş olması gerekmektedir.

Service sınıfı android.app sınıfından türemiştir ve Abstract'tır.

Yerel Servisler(Local Services)

Local bir servis oluşturmak için Service sınıfından türetme yapılabilir.Yerel servis kullanılacaksa onStartCommand'in yazılması gereklidir.

Bir Servisin sınıfı dışında uygulamanın AndroidManifest.xml dosyasında da belirtiliyor olması gerekmektedir. AndroidManifest içerisinde servis bildirimi service isimli bir xml elemanı ile bildirilir.

 <service
  android:name=".MyService"
  android:enabled="true"
  android:exported="true">
</service>

Ide üzerinde New > Service diyerek yeni bir servis yaratılabilir. Menüden ekleme yapıldığında AndroidManifest dosyasına ekleme Android Studio tarafından otomatik yapılmaktadır.İstenirse bu durum manuel olarak da yapılabilmektedir.

Service sınıfının onBind isimli bir tane abstract metodu bulunmaktadır. Dolayısıyla onBind metodu mutlaka implemente edilmek zorundadır.Eğer kullanılması istenmiyorsa boş bırakıp null döndürmek yerine UnsupportedOperationException fırlatılması daha iyi bir teknik olarak düşünülebilir.

Service sınıfı da Context sınıfından türetilmiştir ve uygulamanın ana Threadi içerisinde çalışmaktadır.Dolayısıyla ana Thread içerisinden tüm işlemler yapılabilir.

Örneğin aşağıda yapılan Toast türüne sahip mesajlar sıkıntı yaratmayacaktır;

package org.csystem.samples.samplelocalservice;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.widget.Toast;

public class MyService extends Service 
{

    @Override
    public void onCreate()
    {
        super.onCreate();
        Toast.makeText(this, "MyService.onCreate()", Toast.LENGTH_LONG).show();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId)
    {
        Toast.makeText(this, "MyService.onStartCommand()", Toast.LENGTH_LONG).show();
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy()
    {
        Toast.makeText(this, "MyService.onDestroy()", Toast.LENGTH_LONG).show();
        super.onDestroy();
    }

    @Override
    public IBinder onBind(Intent intent)
    {
        Toast.makeText(this, "MyService.onBind()", Toast.LENGTH_LONG).show();
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }
}

[Dropbox]066-SampleLocalService

066-SampleLocalService ne yapar?

New > Service diyerek yeni Service eklenir projeye.

ve onCreate onStop onDestroy içerisinde Toast mesaj yazdırılır.

Anahtar Notlar : Service sınıfının başlangıç metodu nesne yaratılırken başka bir Threade geçildiğinden burada ana Thread'e ilişkin işlemler yapılmamalıdır.

Local bir Activity'nin durdurulması için ya stopService ya da kendi içerisinde stopSelf isimli metodlar çağrılmalıdır.

onDestroy metodu servis kapatıldığında otomatik çağrılmalıdır.

onStart metodunun geri dönüş değeri ileride ele alınacaktır.

Bir Activity servisi yarattıktan sonra yok olabilir.Bu durumda servis yaşamaya devam eder.Bu tip durumlarda servisin normal sonlandırılması stopSelf isimli metotla yapılabilir.

Bir servis içerisinde Thread kullanmak çok karşılaşılan bir durumdur. Şüphesiz bu açılan Thread ana Thread olmadığından ana Threadle ilgili işlemlerin yapılabilmesi için Threadler arası haberleşme kullanılmalıdır.


Aşağıdaki örnekte N tane çift sayı üretildiğinde servisin otomatik durması sağlanan uygulama yazılmıştır;

[Dropbox]067-ServiceStopSelf

067-ServiceStopSelf ne yapar?

Start stop butonlarıyla kontrol edilir

Serviste Random çift sayılar generate edilir.

Ve üretilen sayılar girdiğimiz sayı kadar olunca servis stop konumuna geçer.


Sınıf Çalışması: Yukarıdaki uygulamayı , sayıları server üzerinden alacak şekilde yeniden yazınız.

[Dropbox]068-NumberServiceTCP

068-NumberServiceTCP ne yapar?

Server kısmı

  • Server consoledan çalıştırılır.
  • Port numarasını konsoldan alır
  • Client 1 gönderirse cevap olarak random bir sayı gönderilir.

Android Kısmı

  • AsyncTask'da socket açılarak server dinlenecek
  • 3 çift sayı geldikten sonra socket kapatılmak için 0 gönderilecek.
  • alınan değerler notification olarak yazdırılacak.

IntentService sınıfı

Bilindiği gibi Service sınıfından türetme yapıldığında service ana Threadte çalışıyor durumundadır.Programcının arkaplanda başka iş yapması için kendi Threadini yaratması gerekir.Ancak bu durum her zaman istenmeyebilir. IntentService sınıfı servis yaratıldıktan sonra otomatik olarak arkaplanda bir Thread açar ayrıca birden fazla servis yaratılması durumunda bu işlemleri kuyruğa sokarak sırasıyla yapılmasını sağlar.Tüm işlemler bittikten sonra stopSelf metodunu kendi çağırır.

IntentService sınıfı da Service sınıfından türetilmiştir.

[Dropbox]069-SimpleIntentService

Anahtar Notlar: Service Sınıfından türetme yapıldığında ve o sınıfa ilişkin servis başlatıldığında ayrı nesneler yaratılır.Eğer programcı servis sınıfı içerisinde thread yaratırsa ayrı threadler yaratmış olur.Bu durumda servis sınıfından türetilen sınıf ile birden fazla servis yaratma durumu MultiThread kullanımla aynı durumdur.Fakat IntentService sınıfından türetilen sınıflar için bir işlem bitmeden diğerine geçilemez.

069-SimpleIntentService Ne Yapar?

Service ile IntentService arasında farkı gösterebilmek için cift sayı generate eden Thread yazıldı.

Normal servisle IntentService arasinda farkı göstermek için onStartCommande Toast mesaj yazıldı.Aynı Threadde olduğu göstermek için.

onHandleIntent farklı bir Threadde olduğu için Handler kullanıldı.

IntentService ile Service arasındaki en büyük fark IntentService'in bir önceki IntentService'i beklemesi(Download kuyruğu gibi) IntentService genelde geri dönüş işlemi olmayan şekildeki uygulamalar içindir.

onHandleIntent metodu arkaplan çalışan yani yeniden yaratılan Threadin içerisinde çalışmak demektir.

IntentService sınıfın default constructoru yoktur.Name parametreli bir başlangıç metodu vardır.Bunun için türetme yapıldığında default constructor programcı tarafından uygun şekilde yazılmalıdır.

[Dropbox]70-IntentServiceQueueHandle

70-IntentServiceQueueHandle Ne Yapar?

Bağlantılı Servisler(Bound Services)

Bağlantılı Servis yaratmanın 3 yolu vardır.

  1. Binder Sınıftan türetme yapmak
  2. Messenger kullanarak servis yaratma
  3. AIDL (Android Interface Defination Language)ile yaratma

1.Binder Sınıftan Türetme Yaparak Servis Oluşturma

Bu tarz oluşturulan servisler aynı uygulama içerisinde erişilebilir bağlantılı servislerdir.Böyle bir servis oluşturmak için tipik adımlar şunlardır.

  • Servis içerisinde Binder nesnesi oluşturmak gerekir.
  • onBind metodunda Binder nesnesiyle geri dönülmelidir.
  • Client uygulama ServiceConnection sınıfının onServiceConnected metodu içerisinde iligili bağlantı referansını elde eder.

[Dropbox]71-LocalBindService

LocalBindService Ne Yapar ?

Servis içerisindeki nextInt metoduna Activity içerisinden erişim sağlar .

MyBindService kalibi yazildiktan sonra MainActivity de MyBindService , ServiceConnection nesneleri olusturulur.

init icerisinde ServiceConnection sınıfının metodları override edilir.

onServiceConnected metodunun IBinder service arayüzünden MyBinder elde edilir.Bu referanstan getService ile servis elde edilir.

Bir flagle de bağlı olup olmadığı kontrol edilir.

Bir servise bağlanmak için Context sınıfının bindService metodu çağrılmalıdır.

    public boolean bindService(Intent service , ServiceConnection conn , int flags)

bindService metodunun

1 .parametresi servise ilişkin Intent referansı

2 .parametresi ServiceConnection referans türünden parametre ,

3 .parametresi ise servis çalışmıyorsa otomatik yaratılıp yaratılmayacağına ilişkin flag parametresidir. Tipik olarak bind işleminde servis unBind olduğu zaman servis durmaktadır.

3.parametre şunlardan biri olabilir; BIND_AUTO_CREATE,BIND_DEBUG_UNBIND,BIND_NOT_FOREGROUND,BIND_ABOVE_CLIENT,BIND_ALLOW_OOM_MANAGEMENT,BIND_WAIVE_PRIORITY .

bindService metodunun geri dönüş değerini bağlanma işleminin başarılı olup olmadığına ilişkin değerdir.Bağlanılan servisten unBindservice metoduyla bağlantı kesilebilir.

UDP/IP Haberleşmesi

2.AIDL Servisler

AIDL servisler aynı mobil cihaz içerisinde 2 ayrı processin haberleşmesi(Interprocess Communication) için kullanılan servislerden biridir. AIDL servisler ile yaratılan servisler asenkron çalışmaktadırlar.

AIDL servisler .aidl uzantılı dosyalar ile oluşturular. .aidl dosyası içerisinde çok küçük farklılıklar olmasına karşın tipik bir Java arayüzü tanımlanır. Bu arayüz içerisinde diğer processler tarafından hizmet alınacak metotlar, abstract metotlar olarak yazılır.

Bunun için Android Studio içerisinde AIDL dosya eklenebilir. Android Studio build sistemi .aidl uzantılı dosyadan .java uzantılı bir dosya generate eder. Bu generete edilen dosyaya programcı dokunmamalıdır. Dokunursa, tekrar generate edildiğinde yaptıklarını kaybedilebilir.

AIDL dosyasından oluşturulan java dosyası detaylı bir dosyadır ancak özetle şu söylenebilir, programcının yazmış olduğu arayüz android.os.IInterface arayüzünden türetilmiş şekilde java dosyası üretilecektir. Bu işlemden sonra generate edilmiş arayüzün taban arayüzü olan IInterface arayüzünün stop isimli arayüzü türünden bir eleman içeren bir servis sınıfı yazılır.

Bu işlem tipik olarak aşağıdaki gibi yapılabilir.

[Dropbox]075-AIDLServices

Yazilacak Kod Öğrenilecek.

Aslında bu işlem servisten yararlanarak diğer process'e add metodunu implemente etmiş olan bir sınıf nesnesi döndürmektir. Şüphesiz bu işlem diğer metotlar içerisinde de yapılabilir.

Diğer process bu servis için android-manifest dosyasında yazılı olan ismini kullanacaktır. (Yani ".SampleService") Bu servisin hangi uygulamadan geldiğini anlayabilmesi için bu uygulamanında kendisini process olarak gösterecektir.

AIDL Servis Kullanan Uygulamanın Tasarımı

[Dropbox]075-AIDLServices/AIDLUserProcess

AIDL servis kullanacak diğer uygulama android-manifest dosyasında uygulama ismini ve servis ismini belirtmelidir.

<service android:name=".SampleService" android:process=".AIDLServiceApp"/>

Client uygulamanın java dosyası içerisinde ServiceConnection sınıfından türetilmiş bir sınıf türünden referans tanımlanmalıdır.

Bu sınıfın onServiceConnected ve onServiceDisconnected metotları içerisinde bir önceki projede oluşturulan arayüz türünden bir referans elde edilmelidir. Şüphesiz bu arayüzün kullanılması için o arayüze ilişkin aidl dosyasına ihtiyaç bulunmaktadır. Bu arayüze ilişkin aidl dosyası diğer projeden bu projeye aktarılmalıdır. Bu aidl dosyası daha sonraki geliştirmelere bağlı olarak ortak bir alandan kullanılabilir.

Servis veren uygulama içerisindeki aidl dosyası hangi paketteyse servis alan uygulama içerisinde de o pakete atılmalıdır.Şüphesiz client uygulamanın paketi(sandbox) ile servis veren uygulamanın paketi farklı olmalıdır.

Bu noktadan sonra artık programcı örneğin hizmet alacağı activity içerisinde bir ServiceConnection nesnesi oluşturulur.

ServiceConnection sınıfının onServiceConnected ve onServiceDisconnected metodları bulunmaktadır.Bu metodlar içerisinde service alınacak arayüze ilişkin referans veri elemanı doldurulur.

onServiceConnected metodu içerisinde hangi arayüzden service alınacaksa o service ilişkin referansın gösterdiği nesne aşağıdaki gibi elde edilebilir.

[Dropbox]076-AIDLServicesParcelable/AIDLClientApp

m_serviceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service)
            {
                m_sampleInterface = ISampleInterface.Stub.asInterface(service);
            }

            @Override
            public void onServiceDisconnected(ComponentName name)
            {
                m_sampleInterface = null;
            }
        };

Burada aslında genel olarak Ibinder arayüzü sanki ISampleInterface arayüzüne dönüştürülmektedir.

onServiceDisconnected metodundaysa servisten bağlantı kesildiğindeki işlemler yapılmaktadır.Tipik olarak service null'a çekilebilir.

Bir servise bağlanmak için aşağıdaki gibi bir kod çalıştırılabilir.

Intent intent = new Intent(ISampleInterface.class.getName());
intent.setPackage("org.csystem.samples.aidlserviceapp");
this.bindService(intent, m_serviceConnection, Context.BIND_AUTO_CREATE);

Burada setPackage metoduyla ilgili servise ilişkin paket belirlenmektedir.

Bu işlem ayrıca Intent sınıfının bir constructor elemanı ile de yapılabilir. Ya da doğrudan arayüz uzun ismiyle verilebilir.

Kullanıcının Belirlediği Türlerin Servise Geçirilmesi

Client program diğer uygulamanın servisini kullanırken Java'nın temel türleri iki servis arasında kullanılabilir.

Bunun yanında aşağıdaki türlerde geçirilebilmektedir.

  1. Aidl String sınıfı ve Charsequence arayüzünü destekler
  2. Başka bir Aidl arayüzü yine Aidl arayüzü içerisinde kullanılabilir.
  3. Kullanıcının belirlediği türler Android.os.Parcelable arayüzünü destekliyorlarsa Aidl servislerde kullanılabilir.
  4. List ve Map tarzı Collection sınıfları javanın temel türlerine ilişkin açılmışlarsa ve ya String , CharSequence sınıfı türünden açılmışsa ve ya Parselable arayüzünü destekleyen açılımda kullanıldıysa.
  5. String dışındaki referans türleri için yön belirteci verilmesi gerekmektedir.Bu yön belirteci in , out , inout şeklinde belirtilir.In belirteci bu türün çağıran tarafından değer atanması gerektiği, out belirteciyle belirtilmişse servis tarafından set edilecek anlamındadır.Bu durumda çağıranın herhangi bir nesne belirtmesine gerek yoktur.Inout belirteciyse hem çağrınanın hem servisin bu referansı set edeceği anlamında kullanımaktadır.Serializable arayüzü burada kullanılmamaktadır.Bunun ile birlikte in,out,inout şeklinde belirtilmeyen referans türleri için aidl servisleri kullanılamamaktadır.

Parcelable arayüzü

Bu arayüzün describeContens ve writeToParcel isimli iki adet abstract metodu bulunmaktadır.Programcı yazdığı sınıf için yazacağı ve okuyacağı Parcellere ilişkin bu metodları override etmelidir.Bununla birlikte programcı Parcalable.CREATOR < T > türünden bir final elemanı nesneyi geri döndürecek şekilde kullanmalıdır.

Parcelable arayüzünü implemente eden bir sınıf içerisinde CREATOR isimli public bir veri elemanı bulunmalıdır.Creator'un kullanımı aşağıdaki gibidir.

public static final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>()
{
   public Person createFromParcel(Parcel in)
   {
     return new Person(in);
   }

   public Person[] newArray(int size) 
   {
     return new Person[size];
   }
};

[Dropbox]076-AIDLServicesParcelable/AIDL

Buna göre creator nesnesiyle Parcelable sınıfını implemente eden türden bir nesne yaratmaktadır.

Parcelable.Creator sinifinin createFromParsel metodu Parcel parametrelidir.

Parcelable sınıfını belirten sınıf genel olarak şu kalıpla yazılabilir.

076-AIDLServicesParcelable/aidlserviceapp/Person.java

package org.csystem.samples.aidlserviceapp;

import android.os.Parcel;
import android.os.Parcelable;

/**
 * Created by oguz on 04.06.2016.
 */
public class Person implements Parcelable {
    private String m_name;

    public Person(Parcel in)
    {
        m_name = in.readString();
    }

    @Override
    public void writeToParcel(Parcel dest, int flags)
    {
        dest.writeString(m_name);
    }

    public String getName()
    {
        return m_name;
    }

    public void setName(String name)
    {
        m_name = name;
    }

    public static final Parcelable.Creator<Person> CREATOR
            = new Parcelable.Creator<Person>() {
        public Person createFromParcel(Parcel in) {
            return new Person(in);
        }

        public Person[] newArray(int size) {
            return new Person[size];
        }
    };
    @Override
    public int describeContents()
    {
        return 0;
    }
}

Parceable arayüzü implemente edilirken kullanılacak olan Person sınıfının da aidl dosyası bulunmalıdır.

Person.aidl dosyasında person sınıfının Parcelable direktifi ile verilmesi gerekmektedir.

Person.aidl

package org.csystem.samples.aidlserviceapp;
parcelable Person;

IPersonInterface.aidl

// IPersonInterface.aidl
package org.csystem.samples.aidlserviceapp;

import org.csystem.samples.aidlserviceapp.Person;
// Declare any non-default types here with import statements

interface IPersonInterface {
     String toUpperCase(in Person p);
}

Burada programcının yazdığı sınıflar için .java ve .aidl dosyalarını programıcı projeye ayrıca dahil etmelidir.Bu servisi kullanacak diğer uygulamaya arayüzün aidl dosyası , prorgramcının yazdığı sınıfın aidl dosyası birlikte götürülecektir.

Anahtar Notlar:

Bu kavram teknik olarak processler arası haberleşme yöntemidir.Processler arası haberleşmede başka tekniklerde uygulanabilmektedir.Bu yönteme android dünyasında aidl servisler denilmektedir.

Person.aidl dosyasının client programa taşınması , client programın bu türden haberi olması içindir.

Benzer şekilde client uygulama ServiceConnection gibi işlemleri yapar. Bir önceki uygulamadan farkı person.java ve person.aidl dosyasının olması ve ilgili senaryonun ona göre tanımlanmasıdır.

Send User Defined Class using AIDL RPC/IPC call (Parcelable)

#Sisteme İlişkin Bazı Elemanların Ele Alınması