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:
- Local Servisler
- 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;
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.
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.
- Binder Sınıftan türetme yapmak
- Messenger kullanarak servis yaratma
- 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.
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.
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.
- Aidl String sınıfı ve Charsequence arayüzünü destekler
- Başka bir Aidl arayüzü yine Aidl arayüzü içerisinde kullanılabilir.
- Kullanıcının belirlediği türler Android.os.Parcelable arayüzünü destekliyorlarsa Aidl servislerde kullanılabilir.
- 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.
- 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)