Android Arayüz Tanımı Dili (AIDL), diğer IDL'lere benzer: istemci ve hizmetin, işlemler arası iletişim (IPC) kullanarak birbirleriyle iletişim kurmak için üzerinde anlaştığı programlama arayüzünü tanımlamanıza olanak tanır.
Android'de bir işlem normalde başka bir işlemin belleğine erişemez. Birbirleriyle iletişim kurabilmeleri için nesnelerini işletim sisteminin anlayabileceği temel öğelere ayırmaları ve bu sınırda nesneleri sizin için sıralamaları gerekir. Bu marshalling işlemini yapacak kodu yazmak zahmetli olduğundan Android, AIDL ile bu işlemi sizin için yapar.
Not: AIDL yalnızca farklı uygulamalardaki istemcilerin IPC için hizmetinize erişmesine izin verirseniz ve hizmetinizde çoklu iş parçacığı işlemeyi kullanmak istiyorsanız gereklidir. Farklı uygulamalarda eşzamanlı IPC gerçekleştirmeniz gerekmiyorsa Binder
uygulayarak arayüzünüzü oluşturun.
IPC gerçekleştirmek istiyorsanız ancak çoklu iş parçacığı işlemeniz gerekmiyorsa arayüzünüzü Messenger
kullanarak uygulayın.
Yine de AIDL uygulamadan önce bağlı hizmetleri anladığınızdan emin olun.
AIDL arayüzünüzü tasarlamaya başlamadan önce, AIDL arayüzüne yapılan çağrıların doğrudan işlev çağrıları olduğunu unutmayın. Çağrının gerçekleştiği ileti dizisi hakkında varsayımlarda bulunmayın. Ne olacağı, aramanın yerel işlemdeki bir ileti dizisinden mi yoksa uzak bir işlemdeki bir ileti dizisinden mi geldiğine bağlı olarak değişir:
- Yerel işlemden yapılan çağrılar, çağrıyı yapan aynı iş parçacığında yürütülür. Bu, ana kullanıcı arayüzü iş parçacığınızsa bu iş parçacığı AIDL arayüzünde çalışmaya devam eder. Başka bir iş parçacığıysa hizmette kodunuzu yürüten iş parçacığı budur. Bu nedenle, hizmete yalnızca yerel iş parçacıkları erişiyorsa hizmette hangi iş parçacıklarının çalıştığını tamamen kontrol edebilirsiniz. Ancak bu durumda AIDL'yi hiç kullanmayın. Bunun yerine,
Binder
uygulayarak arayüzü oluşturun. - Uzak bir süreçten gelen çağrılar, platformun kendi süreciniz içinde tuttuğu bir iş parçacığı havuzundan dağıtılır. Aynı anda gerçekleşen birden fazla arama olan bilinmeyen mesaj dizilerinden gelen aramalara hazırlıklı olun. Diğer bir deyişle, bir AIDL arayüzünün uygulaması tamamen iş parçacığı güvenli olmalıdır. Aynı uzak nesnedeki bir iş parçacığından yapılan çağrılar, alıcı ucunda sıralı gelir.
oneway
anahtar kelimesi, uzak çağrıların davranışını değiştirir. Bu özellik kullanıldığında uzaktan arama engellenmez. İşlem verilerini gönderir ve hemen geri döner. Arayüz uygulaması, bunu normal bir uzaktan çağrı olarakBinder
iş parçacığı havuzundan normal bir çağrı olarak alır.oneway
yerel bir aramayla birlikte kullanılırsa herhangi bir etki olmaz ve arama senkronize olarak devam eder.
AIDL arayüzü tanımlama
AIDL arayüzünüzü Java programlama dili söz dizimini kullanarak bir .aidl
dosyasında tanımlayın, ardından hem hizmeti barındıran uygulamanın hem de hizmete bağlanan diğer uygulamaların kaynak kodundaki src/
dizinine kaydedin.
.aidl
dosyasını içeren her uygulamayı derlediğinizde, Android SDK araçları .aidl
dosyasına dayalı bir IBinder
arayüzü oluşturur ve bunu projenin gen/
dizinine kaydeder. Hizmet, IBinder
arayüzünü uygun şekilde uygulamalıdır. Ardından istemci uygulamalar, IPC gerçekleştirmek için IBinder
üzerinden hizmete bağlanabilir ve yöntemleri çağırabilir.
AIDL'yi kullanarak sınırlı bir hizmet oluşturmak için aşağıdaki bölümlerde açıklanan adımları uygulayın:
.aidl
dosyasını oluşturunBu dosya, yöntem imzaları içeren programlama arayüzünü tanımlar.
- Arayüzü uygulama
Android SDK Tools,
.aidl
dosyanıza göre Java programlama dilinde bir arayüz oluşturur. Bu arayüzde,Binder
sınıfını genişleten ve AIDL arayüzünüzdeki yöntemleri uygulayanStub
adlı bir iç soyut sınıf bulunur.Stub
sınıfını genişletmeniz ve bu yöntemleri uygulamanız gerekir. - Arayüzü istemcilere gösterme
Stub
sınıfını uygulamanızı döndürmek için birService
uygulayın veonBind()
'u geçersiz kılın.
Dikkat: İlk sürümünüzden sonra AIDL arayüzünüzde yaptığınız değişiklikler, hizmetinizi kullanan diğer uygulamaların çalışmasını engellememek için geriye dönük uyumlu kalmalıdır. Yani, .aidl
dosyanız diğer uygulamaların hizmetinizin arayüzüne erişebilmesi için diğer uygulamalara kopyalanacağından orijinal arayüz için desteği sürdürmeniz gerekir.
.aidl dosyasını oluşturma
AIDL, parametre alabilen ve değer döndürebilen bir veya daha fazla yöntemle bir arayüzü belirtmenizi sağlayan basit bir söz dizimi kullanır. Parametreler ve döndürülen değerler, AIDL tarafından oluşturulan diğer arayüzler de dahil olmak üzere herhangi bir türde olabilir.
.aidl
dosyasını Java programlama dilini kullanarak oluşturmanız gerekir. Her .aidl
dosyası tek bir arayüz tanımlamalıdır ve yalnızca arayüz beyanı ile yöntem imzalarını gerektirir.
AIDL varsayılan olarak aşağıdaki veri türlerini destekler:
- Java programlama dilindeki tüm ilkel türler (
int
,long
,char
,boolean
vb.) int[]
veyaMyParcelable[]
gibi her türden diziString
CharSequence
List
List
içindeki tüm öğeler, bu listedeki desteklenen veri türlerinden veya AIDL tarafından oluşturulan diğer arayüzlerden ya da beyan ettiğiniz diğer ürünlerden biri olmalıdır.List
isteğe bağlı olarakList<String>
gibi parametreli bir tür sınıfı olarak kullanılabilir. Yöntem,List
arayüzünü kullanacak şekilde oluşturulsa da diğer tarafın aldığı gerçek somut sınıf her zaman birArrayList
olur.Map
Map
içindeki tüm öğeler, bu listedeki desteklenen veri türlerinden veya tanımladığınız diğer AIDL tarafından oluşturulan arayüzlerden ya da parcelable'lerden biri olmalıdır.Map<String,Integer>
biçimindekiler gibi parametreli tür eşlemeleri desteklenmez. YöntemMap
arayüzünü kullanacak şekilde oluşturulsa da diğer tarafın aldığı gerçek somut sınıf her zaman birHashMap
olur.Map
yerineBundle
kullanabilirsiniz.
Daha önce listelenmeyen her ek tür için, arayüzünüzle aynı pakette tanımlanmış olsalar bile bir import
ifadesi eklemeniz gerekir.
Hizmet arayüzünüzü tanımlarken aşağıdakileri göz önünde bulundurun:
- Yöntemler sıfır veya daha fazla parametre alabilir ve bir değer ya da boşluk döndürebilir.
- Basit olmayan tüm parametreler, verilerin hangi yönde aktığını belirten bir yön etiketi gerektirir:
in
,out
veyainout
(aşağıdaki örneğe bakın).Temel öğeler,
String
,IBinder
ve AIDL tarafından oluşturulan arayüzler varsayılan olarakin
olur ve başka bir şekilde olamaz.Dikkat: Yönlendirmeyi, gerçekten ihtiyaç duyulanla sınırlayın. Çünkü parametrelerin düzenlenmesi pahalıdır.
- İçe aktarma ve paket beyanlarından önceki yorumlar hariç olmak üzere,
.aidl
dosyasına dahil edilen tüm kod yorumları oluşturulanIBinder
arayüzüne dahil edilir. - Dize ve int sabitleri, AIDL arayüzünde
const int VERSION = 1;
gibi tanımlanabilir. - Yöntem çağrıları, normalde arayüzdeki bir yöntem dizinini temel alan bir
transact()
kodu tarafından gönderilir. Bu durum sürüm oluşturmayı zorlaştırdığından, işlem kodunu bir yönteme manuel olarak atayabilirsiniz:void method() = 10;
. - Boş bırakılabilir bağımsız değişkenler ve döndürülen türlerin
@nullable
ile eklenmesi gerekir.
Aşağıda bir .aidl
dosyası örneği verilmiştir:
// IRemoteService.aidl package com.example.android; // Declare any non-default types here with import statements. /** Example service interface */ interface IRemoteService { /** Request the process ID of this service. */ int getPid(); /** Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString); }
.aidl
dosyanızı projenizin src/
dizinine kaydedin. Uygulamanızı oluşturduğunuzda SDK araçları, projenizin gen/
dizininde IBinder
arayüz dosyasını oluşturur. Oluşturulan dosyanın adı, .aidl
dosyasının adıyla eşleşir ancak .java
uzantısına sahiptir. Örneğin, IRemoteService.aidl
IRemoteService.java
sonucunu verir.
Android Studio kullanıyorsanız artımlı derleme neredeyse anında bağlayıcı sınıfı oluşturur.
Android Studio kullanmıyorsanız Gradle aracı, uygulamanızı bir sonraki sefer oluşturduğunuzda bağlayıcı sınıfını oluşturur. Kodunuzun oluşturulan sınıfa bağlantı oluşturabilmesi için .aidl
dosyasını yazmayı bitirir bitirmez projenizi gradle assembleDebug
veya gradle assembleRelease
ile derleyin.
Arayüzü uygulama
Uygulamanızı oluşturduğunuzda Android SDK araçları, .aidl
dosyanızı temel alan bir .java
arayüz dosyası oluşturur. Oluşturulan arayüz, YourInterface.Stub
gibi üst arayüzünün soyut bir uygulaması olan ve .aidl
dosyasındaki tüm yöntemleri açıklayan Stub
adlı bir alt sınıf içerir.
Not: Stub
, birkaç yardımcı yöntem de tanımlar. Bunlardan en önemlisi, genellikle istemcinin onServiceConnected()
geri çağırma yöntemine iletilen bir IBinder
alan ve stub arayüzünün bir örneğini döndüren asInterface()
yöntemidir. Bu yayını yapma hakkında daha fazla bilgi için IPC yöntemi çağırma bölümüne bakın.
.aidl
öğesinden oluşturulan arayüzü uygulamak için oluşturulan Binder
arayüzünü (ör. YourInterface.Stub
) genişletin ve .aidl
dosyasından devralınan yöntemleri uygulayın.
Aşağıda, önceki IRemoteService.aidl
örneğinde tanımlanan IRemoteService
adlı bir arayüzün anonim bir örnek kullanılarak uygulandığı örnek verilmiştir:
Kotlin
private val binder = object : IRemoteService.Stub() { override fun getPid(): Int = Process.myPid() override fun basicTypes( anInt: Int, aLong: Long, aBoolean: Boolean, aFloat: Float, aDouble: Double, aString: String ) { // Does nothing. } }
Java
private final IRemoteService.Stub binder = new IRemoteService.Stub() { public int getPid(){ return Process.myPid(); } public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) { // Does nothing. } };
Artık binder
, hizmetin IPC arayüzünü tanımlayan Stub
sınıfının bir örneğidir (Binder
). Sonraki adımda, bu örnek istemcilere gösterilir. Böylece istemciler hizmetle etkileşime geçebilir.
AIDL arayüzünüzü uygularken birkaç kuralı göz önünde bulundurun:
- Gelen aramaların ana iş parçacığında yürütülmesi garanti edilmez. Bu nedenle, baştan itibaren çoklu iş parçacığı kullanmayı düşünmeniz ve hizmetinizi iş parçacığı güvenliğine uygun olacak şekilde doğru şekilde oluşturmanız gerekir.
- Varsayılan olarak IPC çağrıları eşzamanlıdır. Hizmetin bir isteği tamamlamasının birkaç milisaniyeden uzun sürdüğünü biliyorsanız hizmeti işlemin ana iş parçacığında çağırmayın. Bu durum, uygulamanın kilitlenmesine ve Android'in "Uygulama Yanıt Vermiyor" iletişim kutusunu görüntülemesine neden olabilir. İstemcide ayrı bir mesaj dizisinden çağırın.
- Yalnızca
Parcel.writeException()
ile ilgili referans dokümanlarında listelenen istisna türleri, arayana geri gönderilir.
Arayüzü istemcilere gösterme
Hizmetinizin arayüzünü uyguladıktan sonra, istemcilerin bağlanabilmesi için arayüzü istemcilere göstermeniz gerekir. Hizmetinizin arayüzünü göstermek için Service
sınıfını genişletin ve önceki bölümde açıklandığı gibi, oluşturulan Stub
sınıfını uygulayan sınıfınızın bir örneğini döndürmek için onBind()
sınıfını uygulayın. Aşağıda, IRemoteService
örnek arayüzünü istemcilere gösteren bir örnek hizmet verilmiştir.
Kotlin
class RemoteService : Service() { override fun onCreate() { super.onCreate() } override fun onBind(intent: Intent): IBinder { // Return the interface. return binder } private val binder = object : IRemoteService.Stub() { override fun getPid(): Int { return Process.myPid() } override fun basicTypes( anInt: Int, aLong: Long, aBoolean: Boolean, aFloat: Float, aDouble: Double, aString: String ) { // Does nothing. } } }
Java
public class RemoteService extends Service { @Override public void onCreate() { super.onCreate(); } @Override public IBinder onBind(Intent intent) { // Return the interface. return binder; } private final IRemoteService.Stub binder = new IRemoteService.Stub() { public int getPid(){ return Process.myPid(); } public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) { // Does nothing. } }; }
Artık etkinlik gibi bir istemci bu hizmete bağlanmak için bindService()
yöntemini çağırdığında, istemcinin onServiceConnected()
geri çağırması, hizmetin onBind()
yöntemi tarafından döndürülen binder
örneğini alır.
İstemcinin arayüz sınıfına da erişimi olmalıdır. Dolayısıyla istemci ve hizmet ayrı uygulamalardaysa istemcinin uygulamasında src/
dizininde .aidl
dosyasının bir kopyası olmalıdır. Bu kopya, android.os.Binder
arayüzünü oluşturur ve istemciye AIDL yöntemlerine erişim sağlar.
Müşteri, onServiceConnected()
geri çağırma işlevinde IBinder
aldığında, döndürülen parametreyi YourServiceInterface
türüne dönüştürmek için YourServiceInterface.Stub.asInterface(service)
işlevini çağırmalıdır:
Kotlin
var iRemoteService: IRemoteService? = null val mConnection = object : ServiceConnection { // Called when the connection with the service is established. override fun onServiceConnected(className: ComponentName, service: IBinder) { // Following the preceding example for an AIDL interface, // this gets an instance of the IRemoteInterface, which we can use to call on the service. iRemoteService = IRemoteService.Stub.asInterface(service) } // Called when the connection with the service disconnects unexpectedly. override fun onServiceDisconnected(className: ComponentName) { Log.e(TAG, "Service has unexpectedly disconnected") iRemoteService = null } }
Java
IRemoteService iRemoteService; private ServiceConnection mConnection = new ServiceConnection() { // Called when the connection with the service is established. public void onServiceConnected(ComponentName className, IBinder service) { // Following the preceding example for an AIDL interface, // this gets an instance of the IRemoteInterface, which we can use to call on the service. iRemoteService = IRemoteService.Stub.asInterface(service); } // Called when the connection with the service disconnects unexpectedly. public void onServiceDisconnected(ComponentName className) { Log.e(TAG, "Service has unexpectedly disconnected"); iRemoteService = null; } };
Daha fazla örnek kod için
ApiDemos'taki
RemoteService.java
sınıfına bakın.
IPC üzerinden nesne iletme
Android 10'da (API düzeyi 29 veya üstü) Parcelable
nesnelerini doğrudan AIDL'de tanımlayabilirsiniz. AIDL arayüz bağımsız değişkenleri ve diğer ayrıştırılabilir öğeler olarak desteklenen türler de burada desteklenir. Bu sayede, marshalling kodunu ve özel sınıfı manuel olarak yazmak için ek iş gerekmez. Ancak bu işlem, boş bir yapı da oluşturur. Özel erişim sağlayıcılar veya başka bir işlev isteniyorsa bunun yerine Parcelable
uygulayın.
package android.graphics; // Declare Rect so AIDL can find it and knows that it implements // the parcelable protocol. parcelable Rect { int left; int top; int right; int bottom; }
Önceki kod örneği, left
, top
, right
ve bottom
tam sayı alanlarına sahip bir Java sınıfını otomatik olarak oluşturur. Alakalı tüm düzenleme kodları otomatik olarak uygulanır ve nesne herhangi bir uygulama eklemek zorunda kalmadan doğrudan kullanılabilir.
Ayrıca, bir IPC arayüzü aracılığıyla özel bir sınıfı bir işlemden diğerine gönderebilirsiniz. Ancak sınıfınızın kodunun IPC kanalının diğer tarafında kullanılabildiğinden emin olun ve sınıfınız Parcelable
arayüzünü desteklemelidir. Parcelable
desteklemek, Android sisteminin nesneleri işlemler arasında düzenlenebilecek temel öğelere ayırmasına olanak tanıdığı için önemlidir.
Parcelable
özelliğini destekleyen özel bir sınıf oluşturmak için aşağıdakileri yapın:
- Sınıfınızın
Parcelable
arayüzünü uygulamasını sağlayın. - Nesnenin mevcut durumunu alıp bir
Parcel
'a yazanwriteToParcel
işlevini uygulayın. - Sınıfınıza
Parcelable.Creator
arayüzünü uygulayan bir nesne olanCREATOR
adlı statik bir alan ekleyin. - Son olarak, aşağıdaki
Rect.aidl
dosyasında gösterildiği gibi, paketlenebilir sınıfınızı açıklayan bir.aidl
dosyası oluşturun.Özel bir derleme süreci kullanıyorsanız
.aidl
dosyasını derlemenize eklemeyin. C dilinde bir başlık dosyasına benzer şekilde, bu.aidl
dosyası derlenmez.
AIDL, oluşturduğu kodda bu yöntemleri ve alanları kullanarak nesnelerinizi sıralar ve sıfırlar.
Örneğin, aşağıdaki Rect.aidl
dosyası, bölünebilir bir Rect
sınıfı oluşturmak için kullanılır:
package android.graphics; // Declare Rect so AIDL can find it and knows that it implements // the parcelable protocol. parcelable Rect;
Aşağıda, Rect
sınıfının Parcelable
protokolünü nasıl uyguladığına dair bir örnek verilmiştir.
Kotlin
import android.os.Parcel import android.os.Parcelable class Rect() : Parcelable { var left: Int = 0 var top: Int = 0 var right: Int = 0 var bottom: Int = 0 companion object CREATOR : Parcelable.Creator<Rect> { override fun createFromParcel(parcel: Parcel): Rect { return Rect(parcel) } override fun newArray(size: Int): Array<Rect?> { return Array(size) { null } } } private constructor(inParcel: Parcel) : this() { readFromParcel(inParcel) } override fun writeToParcel(outParcel: Parcel, flags: Int) { outParcel.writeInt(left) outParcel.writeInt(top) outParcel.writeInt(right) outParcel.writeInt(bottom) } private fun readFromParcel(inParcel: Parcel) { left = inParcel.readInt() top = inParcel.readInt() right = inParcel.readInt() bottom = inParcel.readInt() } override fun describeContents(): Int { return 0 } }
Java
import android.os.Parcel; import android.os.Parcelable; public final class Rect implements Parcelable { public int left; public int top; public int right; public int bottom; public static final Parcelable.Creator<Rect> CREATOR = new Parcelable.Creator<Rect>() { public Rect createFromParcel(Parcel in) { return new Rect(in); } public Rect[] newArray(int size) { return new Rect[size]; } }; public Rect() { } private Rect(Parcel in) { readFromParcel(in); } public void writeToParcel(Parcel out, int flags) { out.writeInt(left); out.writeInt(top); out.writeInt(right); out.writeInt(bottom); } public void readFromParcel(Parcel in) { left = in.readInt(); top = in.readInt(); right = in.readInt(); bottom = in.readInt(); } public int describeContents() { return 0; } }
Rect
sınıfında sıralama basittir. Parcel
değerine yazabileceğimiz diğer değer türlerini görmek için Parcel
'teki diğer yöntemlere göz atın.
Uyarı: Diğer işlemlerden veri almanın güvenlikle ilgili sonuçlarını göz önünde bulundurun. Bu durumda Rect
, Parcel
'dan dört sayı okur ancak bunların, arayanın yapmaya çalıştığı işlem için kabul edilebilir değer aralığında olduğundan emin olmak size aittir. Uygulamanızı kötü amaçlı yazılımlara karşı nasıl koruyacağınız hakkında daha fazla bilgi için Güvenlik ipuçları bölümüne bakın.
Parcelable içeren Bundle bağımsız değişkenlerine sahip yöntemler
Bir yöntem, ayrıştırılabilir öğe içermesi beklenen birBundle
nesnesini kabul ediyorsa Bundle
öğesinden okumayı denemeden önce Bundle.setClassLoader(ClassLoader)
yöntemini çağırarak Bundle
sınıf yükleyicisini ayarladığınızdan emin olun. Aksi takdirde, paketlenebilir uygulamanızda doğru tanımlanmış olsa bile ClassNotFoundException
hatasıyla karşılaşırsınız.
Örneğin, aşağıdaki örnek .aidl
dosyasını ele alalım:
// IRectInsideBundle.aidl package com.example.android; /** Example service interface */ interface IRectInsideBundle { /** Rect parcelable is stored in the bundle with key "rect". */ void saveRect(in Bundle bundle); }
Rect
okunmadan önce ClassLoader
, Bundle
içinde açıkça ayarlanır:
Kotlin
private val binder = object : IRectInsideBundle.Stub() { override fun saveRect(bundle: Bundle) { bundle.classLoader = classLoader val rect = bundle.getParcelable<Rect>("rect") process(rect) // Do more with the parcelable. } }
Java
private final IRectInsideBundle.Stub binder = new IRectInsideBundle.Stub() { public void saveRect(Bundle bundle){ bundle.setClassLoader(getClass().getClassLoader()); Rect rect = bundle.getParcelable("rect"); process(rect); // Do more with the parcelable. } };
IPC yöntemi çağırma
AIDL ile tanımlanmış bir uzak arayüzü çağırmak için görüşme sınıfınızda aşağıdaki adımları uygulayın:
.aidl
dosyasınısrc/
projesinin dizinine ekleyin.- AIDL'ye göre oluşturulan
IBinder
arayüzünün bir örneğini tanımlayın. ServiceConnection
'ü uygulayın.ServiceConnection
uygulamanızı göndererekContext.bindService()
'ü arayın.onServiceConnected()
uygulamanızda,service
adlı birIBinder
örneği alırsınız. Döndürülen parametreyiYourInterface
türüne yayınlamak içinYourInterfaceName.Stub.asInterface((IBinder)service)
komutunu çağırın.- Arayüzünüzde tanımladığınız yöntemleri çağırın. Bağlantı kesildiğinde oluşturulan
DeadObjectException
istisnalarını her zaman yakalayın. Ayrıca, IPC yöntem çağrısında yer alan iki işlemde çakışan AIDL tanımları olduğunda tetiklenenSecurityException
istisnalarını da yakalayabilirsiniz. - Bağlantıyı kesmek için arayüzünüzün örneğini kullanarak
Context.unbindService()
işlevini çağırın.
IPC hizmetini çağırırken aşağıdaki noktaları göz önünde bulundurun:
- Nesneler, süreçler genelinde referans olarak sayılır.
- Anonim nesneleri yöntem bağımsız değişkenleri olarak gönderebilirsiniz.
Bir hizmete bağlama hakkında daha fazla bilgi için Bağlı hizmetlere genel bakış başlıklı makaleyi okuyun.
Aşağıda, ApiDemos projesindeki Remote Service örneğinden alınan, AIDL ile oluşturulmuş bir hizmeti çağırma işlemini gösteren örnek bir kod verilmiştir.
Kotlin
private const val BUMP_MSG = 1 class Binding : Activity() { /** The primary interface you call on the service. */ private var mService: IRemoteService? = null /** Another interface you use on the service. */ internal var secondaryService: ISecondary? = null private lateinit var killButton: Button private lateinit var callbackText: TextView private lateinit var handler: InternalHandler private var isBound: Boolean = false /** * Class for interacting with the main interface of the service. */ private val mConnection = object : ServiceConnection { override fun onServiceConnected(className: ComponentName, service: IBinder) { // This is called when the connection with the service is // established, giving us the service object we can use to // interact with the service. We are communicating with our // service through an IDL interface, so get a client-side // representation of that from the raw service object. mService = IRemoteService.Stub.asInterface(service) killButton.isEnabled = true callbackText.text = "Attached." // We want to monitor the service for as long as we are // connected to it. try { mService?.registerCallback(mCallback) } catch (e: RemoteException) { // In this case, the service crashes before we can // do anything with it. We can count on soon being // disconnected (and then reconnected if it can be restarted) // so there is no need to do anything here. } // As part of the sample, tell the user what happened. Toast.makeText( this@Binding, R.string.remote_service_connected, Toast.LENGTH_SHORT ).show() } override fun onServiceDisconnected(className: ComponentName) { // This is called when the connection with the service is // unexpectedly disconnected—that is, its process crashed. mService = null killButton.isEnabled = false callbackText.text = "Disconnected." // As part of the sample, tell the user what happened. Toast.makeText( this@Binding, R.string.remote_service_disconnected, Toast.LENGTH_SHORT ).show() } } /** * Class for interacting with the secondary interface of the service. */ private val secondaryConnection = object : ServiceConnection { override fun onServiceConnected(className: ComponentName, service: IBinder) { // Connecting to a secondary interface is the same as any // other interface. secondaryService = ISecondary.Stub.asInterface(service) killButton.isEnabled = true } override fun onServiceDisconnected(className: ComponentName) { secondaryService = null killButton.isEnabled = false } } private val mBindListener = View.OnClickListener { // Establish a couple connections with the service, binding // by interface names. This lets other applications be // installed that replace the remote service by implementing // the same interface. val intent = Intent(this@Binding, RemoteService::class.java) intent.action = IRemoteService::class.java.name bindService(intent, mConnection, Context.BIND_AUTO_CREATE) intent.action = ISecondary::class.java.name bindService(intent, secondaryConnection, Context.BIND_AUTO_CREATE) isBound = true callbackText.text = "Binding." } private val unbindListener = View.OnClickListener { if (isBound) { // If we have received the service, and hence registered with // it, then now is the time to unregister. try { mService?.unregisterCallback(mCallback) } catch (e: RemoteException) { // There is nothing special we need to do if the service // crashes. } // Detach our existing connection. unbindService(mConnection) unbindService(secondaryConnection) killButton.isEnabled = false isBound = false callbackText.text = "Unbinding." } } private val killListener = View.OnClickListener { // To kill the process hosting the service, we need to know its // PID. Conveniently, the service has a call that returns // that information. try { secondaryService?.pid?.also { pid -> // Note that, though this API lets us request to // kill any process based on its PID, the kernel // still imposes standard restrictions on which PIDs you // can actually kill. Typically this means only // the process running your application and any additional // processes created by that app, as shown here. Packages // sharing a common UID are also able to kill each // other's processes. Process.killProcess(pid) callbackText.text = "Killed service process." } } catch (ex: RemoteException) { // Recover gracefully from the process hosting the // server dying. // For purposes of this sample, put up a notification. Toast.makeText(this@Binding, R.string.remote_call_failed, Toast.LENGTH_SHORT).show() } } // ---------------------------------------------------------------------- // Code showing how to deal with callbacks. // ---------------------------------------------------------------------- /** * This implementation is used to receive callbacks from the remote * service. */ private val mCallback = object : IRemoteServiceCallback.Stub() { /** * This is called by the remote service regularly to tell us about * new values. Note that IPC calls are dispatched through a thread * pool running in each process, so the code executing here is * NOT running in our main thread like most other things. So, * to update the UI, we need to use a Handler to hop over there. */ override fun valueChanged(value: Int) { handler.sendMessage(handler.obtainMessage(BUMP_MSG, value, 0)) } } /** * Standard initialization of this activity. Set up the UI, then wait * for the user to interact with it before doing anything. */ override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.remote_service_binding) // Watch for button taps. var button: Button = findViewById(R.id.bind) button.setOnClickListener(mBindListener) button = findViewById(R.id.unbind) button.setOnClickListener(unbindListener) killButton = findViewById(R.id.kill) killButton.setOnClickListener(killListener) killButton.isEnabled = false callbackText = findViewById(R.id.callback) callbackText.text = "Not attached." handler = InternalHandler(callbackText) } private class InternalHandler( textView: TextView, private val weakTextView: WeakReference<TextView> = WeakReference(textView) ) : Handler() { override fun handleMessage(msg: Message) { when (msg.what) { BUMP_MSG -> weakTextView.get()?.text = "Received from service: ${msg.arg1}" else -> super.handleMessage(msg) } } } }
Java
public static class Binding extends Activity { /** The primary interface we are calling on the service. */ IRemoteService mService = null; /** Another interface we use on the service. */ ISecondary secondaryService = null; Button killButton; TextView callbackText; private InternalHandler handler; private boolean isBound; /** * Standard initialization of this activity. Set up the UI, then wait * for the user to interact with it before doing anything. */ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.remote_service_binding); // Watch for button taps. Button button = (Button)findViewById(R.id.bind); button.setOnClickListener(mBindListener); button = (Button)findViewById(R.id.unbind); button.setOnClickListener(unbindListener); killButton = (Button)findViewById(R.id.kill); killButton.setOnClickListener(killListener); killButton.setEnabled(false); callbackText = (TextView)findViewById(R.id.callback); callbackText.setText("Not attached."); handler = new InternalHandler(callbackText); } /** * Class for interacting with the main interface of the service. */ private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { // This is called when the connection with the service is // established, giving us the service object we can use to // interact with the service. We are communicating with our // service through an IDL interface, so get a client-side // representation of that from the raw service object. mService = IRemoteService.Stub.asInterface(service); killButton.setEnabled(true); callbackText.setText("Attached."); // We want to monitor the service for as long as we are // connected to it. try { mService.registerCallback(mCallback); } catch (RemoteException e) { // In this case the service crashes before we can even // do anything with it. We can count on soon being // disconnected (and then reconnected if it can be restarted) // so there is no need to do anything here. } // As part of the sample, tell the user what happened. Toast.makeText(Binding.this, R.string.remote_service_connected, Toast.LENGTH_SHORT).show(); } public void onServiceDisconnected(ComponentName className) { // This is called when the connection with the service is // unexpectedly disconnected—that is, its process crashed. mService = null; killButton.setEnabled(false); callbackText.setText("Disconnected."); // As part of the sample, tell the user what happened. Toast.makeText(Binding.this, R.string.remote_service_disconnected, Toast.LENGTH_SHORT).show(); } }; /** * Class for interacting with the secondary interface of the service. */ private ServiceConnection secondaryConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { // Connecting to a secondary interface is the same as any // other interface. secondaryService = ISecondary.Stub.asInterface(service); killButton.setEnabled(true); } public void onServiceDisconnected(ComponentName className) { secondaryService = null; killButton.setEnabled(false); } }; private OnClickListener mBindListener = new OnClickListener() { public void onClick(View v) { // Establish a couple connections with the service, binding // by interface names. This lets other applications be // installed that replace the remote service by implementing // the same interface. Intent intent = new Intent(Binding.this, RemoteService.class); intent.setAction(IRemoteService.class.getName()); bindService(intent, mConnection, Context.BIND_AUTO_CREATE); intent.setAction(ISecondary.class.getName()); bindService(intent, secondaryConnection, Context.BIND_AUTO_CREATE); isBound = true; callbackText.setText("Binding."); } }; private OnClickListener unbindListener = new OnClickListener() { public void onClick(View v) { if (isBound) { // If we have received the service, and hence registered with // it, then now is the time to unregister. if (mService != null) { try { mService.unregisterCallback(mCallback); } catch (RemoteException e) { // There is nothing special we need to do if the service // crashes. } } // Detach our existing connection. unbindService(mConnection); unbindService(secondaryConnection); killButton.setEnabled(false); isBound = false; callbackText.setText("Unbinding."); } } }; private OnClickListener killListener = new OnClickListener() { public void onClick(View v) { // To kill the process hosting our service, we need to know its // PID. Conveniently, our service has a call that returns // that information. if (secondaryService != null) { try { int pid = secondaryService.getPid(); // Note that, though this API lets us request to // kill any process based on its PID, the kernel // still imposes standard restrictions on which PIDs you // can actually kill. Typically this means only // the process running your application and any additional // processes created by that app as shown here. Packages // sharing a common UID are also able to kill each // other's processes. Process.killProcess(pid); callbackText.setText("Killed service process."); } catch (RemoteException ex) { // Recover gracefully from the process hosting the // server dying. // For purposes of this sample, put up a notification. Toast.makeText(Binding.this, R.string.remote_call_failed, Toast.LENGTH_SHORT).show(); } } } }; // ---------------------------------------------------------------------- // Code showing how to deal with callbacks. // ---------------------------------------------------------------------- /** * This implementation is used to receive callbacks from the remote * service. */ private IRemoteServiceCallback mCallback = new IRemoteServiceCallback.Stub() { /** * This is called by the remote service regularly to tell us about * new values. Note that IPC calls are dispatched through a thread * pool running in each process, so the code executing here is * NOT running in our main thread like most other things. So, * to update the UI, we need to use a Handler to hop over there. */ public void valueChanged(int value) { handler.sendMessage(handler.obtainMessage(BUMP_MSG, value, 0)); } }; private static final int BUMP_MSG = 1; private static class InternalHandler extends Handler { private final WeakReference<TextView> weakTextView; InternalHandler(TextView textView) { weakTextView = new WeakReference<>(textView); } @Override public void handleMessage(Message msg) { switch (msg.what) { case BUMP_MSG: TextView textView = weakTextView.get(); if (textView != null) { textView.setText("Received from service: " + msg.arg1); } break; default: super.handleMessage(msg); } } } }