Android Arayüz Tanımlama Dili (AIDL), diğer IDL'lere benzer: Ara işlem iletişimi (IPC) kullanarak birbirleriyle iletişim kurmak için hem istemcinin hem de hizmetin üzerinde anlaştığı programlama arayüzünü tanımlamanızı sağlar.
Android'de, bir işlem normalde başka bir işlemin belleğine erişemez. Konuşmak için nesneleri, işletim sisteminin anlayabileceği ve sizin için söz konusu sınırın ötesindeki nesnelere ayırabileceği temel öğelere ayırmaları gerekir. Bu işlemi yapmak için gereken kodu yazmak yorucu olduğundan Android, AIDL ile bunu sizin için halleder.
Not: AIDL yalnızca farklı uygulamalardan gelen istemcilerin IPC için hizmetinize erişmesine izin verirseniz ve hizmetinizde çoklu iş parçacıklarını işlemek istiyorsanız gereklidir. Farklı uygulamalarda eş zamanlı IPC gerçekleştirmeniz gerekmiyorsa bir Binder
uygulayarak arayüzünüzü oluşturun.
IPC gerçekleştirmek istiyorsanız ancak çoklu iş parçacıklarını işlemeniz gerekmiyorsa Messenger
kullanarak arayüzünüzü uygulayın.
Her şeye rağmen, bir 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 iş parçacığı hakkında varsayımda bulunmayın. Ne olacağı, çağrının yerel işlemdeki bir iş parçacığından mı yoksa uzak bir işlemden mi geldiğine bağlı olarak değişir:
- Yerel işlemden yapılan çağrılar, aramayı yapan 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 yürütülmeye devam eder. Başka bir iş parçacığı söz konusuysa kodunuzu hizmette yürüten iş parçacığıdır. Bu nedenle, hizmete yalnızca yerel iş parçacıkları erişiyorsa hizmette hangi iş parçacıklarının yürütüldüğünü tamamen kontrol edebilirsiniz. Ancak bu durumda hiç AIDL kullanmayın. Bunun yerine, arayüzü bir
Binder
uygulayarak oluşturun. - Uzak bir işlemden gelen çağrılar, platformun kendi sürecinizde tuttuğu bir iş parçacığı havuzundan gönderilir. Birden fazla arama aynı anda gerçekleştiğinden, bilinmeyen ileti dizilerinden gelen aramalara karşı hazırlıklı olun. Başka bir deyişle, AIDL arayüzünün uygulanması tamamen iş parçacığı açısından güvenli olmalıdır. Aynı uzak nesnedeki bir iş parçacığından yapılan çağrılar, alıcı tarafında sırasıyla gelir.
oneway
anahtar kelimesi, uzaktan aramaların davranışını değiştirir. Bu numara kullanıldığında uzaktan çağrı engellenmez. İşlem verilerini gönderir ve hemen geri döner. Arayüz uygulaması sonuçta bunuBinder
iş parçacığı havuzundan normal bir uzak çağrı gibi normal bir çağrı olarak alır.oneway
yerel bir çağrıyla kullanılıyorsa bunun herhangi bir etkisi olmaz ve arama yine de eşzamanlı 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 hizmeti barındıran uygulamanın ve hizmete bağlanan diğer tüm uygulamaların kaynak kodundaki src/
dizinine kaydedin.
.aidl
dosyasını içeren her uygulamayı derlediğinizde Android SDK araçları .aidl
dosyasını temel alan bir IBinder
arayüzü oluşturur ve bunu projenin gen/
dizinine kaydeder. Hizmet, IBinder
arayüzünü uygun şekilde uygulamalıdır. Daha sonra istemci uygulamalar, IPC gerçekleştirmek için hizmete bağlanabilir ve IBinder
üzerinden yöntemler çağırabilir.
AIDL 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şturmaBu dosya, yöntem imzalarıyla programlama arayüzünü tanımlar.
- Arayüzü uygulama
Android SDK araçları,
.aidl
dosyanıza göre Java programlama dilinde bir arayüz oluşturur. Bu arayüzde,Binder
kapsamını genişleten ve AIDL arayüzünüzdeki yöntemleri uygulayanStub
adlı iç soyut sınıf vardır.Stub
sınıfını genişletmeniz ve yöntemleri uygulamanız gerekir. - Arayüzü istemcilere sunma
Stub
sınıfı uygulamanızı döndürmek içinService
uygulayın veonBind()
geçersiz kılın.
Dikkat: İlk sürümünüzden sonra AIDL arayüzünde yaptığınız değişiklikler, hizmetinizi kullanan diğer uygulamaların bozulmasını önlemek için geriye dönük uyumlu kalmalıdır. Diğer bir deyişle, hizmetinizin arayüzüne erişebilmeleri için .aidl
dosyanızın diğer uygulamalara kopyalanması gerektiğinden, orijinal arayüz desteğini sürdürmeniz gerekir.
.aidl dosyasını oluşturma
AIDL, parametre alabilen ve değer döndürebilen bir veya daha fazla yöntem içeren bir arayüz bildirmenize olanak tanıyan basit bir söz dizimi kullanır. Parametreler ve dönüş değerleri, 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şturmalısınız. 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 temel türler (
int
,long
,char
,boolean
vb.) int[]
gibi basit tür dizileriString
CharSequence
List
List
içindeki tüm öğeler, bu listede desteklenen veri türlerinden biri veya beyan ettiğiniz AIDL tarafından oluşturulan diğer arayüzlerden ya da paketlerden biri olmalıdır.List
, isteğe bağlı olarakList<String>
gibi parametreleştirilmiş bir tür sınıfı olarak kullanılabilir. Diğer tarafın aldığı gerçek somut sınıf her zamanArrayList
olur, ancak yöntemList
arayüzünü kullanmak için oluşturulmuştur.Map
Map
içindeki tüm öğeler, bu listede desteklenen veri türlerinden biri veya beyan ettiğiniz AIDL tarafından oluşturulan diğer arayüzlerden ya da paketlerden biri olmalıdır.Map<String,Integer>
biçimindeki haritalar gibi parametreleştirilmiş tür haritalar desteklenmez. Diğer tarafın aldığı gerçek somut sınıf her zamanHashMap
olur, ancak yöntemMap
arayüzünü kullanacak şekilde oluşturulur.Map
yerine birBundle
kullanabilirsiniz.
Arayüzünüzle aynı pakette tanımlanmış olsalar bile, daha önce listelenmeyen her ek tür için bir import
ifadesi eklemeniz gerekir.
Hizmet arayüzünüzü tanımlarken aşağıdaki hususlara dikkat edin:
- Yöntemler sıfır veya daha fazla parametre alabilir ve bir değer ya da boşluk döndürebilir.
- Temel olmayan tüm parametreler, verilerin nereye gideceğini belirten bir yön etiketi gerektirir:
in
,out
veyainout
(aşağıdaki örneğe bakın).Temel kavramlar,
String
,IBinder
ve AIDL tarafından oluşturulan arayüzler varsayılan olarakin
'dir ve farklı olamaz.Dikkat: Marşal parametreleri pahalı olduğundan, yönü gerçekten gerekenlerle sınırlandırın.
- İçe aktarma ve paket ifadelerinden önceki yorumlar hariç,
.aidl
dosyasındaki tüm kod yorumları, oluşturulanIBinder
arayüzüne dahil edilir. - Dize ve tam sayı sabitleri, AIDL arayüzünde
const int VERSION = 1;
gibi tanımlanabilir. - Yöntem çağrıları, normalde arayüzdeki bir yöntem dizinine dayalı olan bir
transact()
kodu ile gönderilir. Bu durum sürüm oluşturmayı zorlaştırdığından işlem kodunu şu yönteme manuel olarak atayabilirsiniz:void method() = 10;
. - Boş değer atanabilir bağımsız değişkenler ve dönüş türlerine
@nullable
kullanılarak ek açıklama eklenmelidir.
Örnek .aidl
dosyası:
// 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ştururken 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şiyor ancak .java
uzantısına sahip. Örneğin IRemoteService.aidl
, IRemoteService.java
ile sonuçlanır.
Android Studio kullanıyorsanız artımlı derleme, bağlayıcı sınıfını neredeyse anında oluşturur.
Android Studio kullanmıyorsanız Gradle aracı, uygulamanızı oluşturduğunuz bir sonraki seferde bağlayıcı sınıfını oluşturur. .aidl
dosyasını yazmayı bitirir bitirmez gradle assembleDebug
veya gradle assembleRelease
ile projenizi oluşturun, böylece kodunuz oluşturulan sınıfla bağlantı kurabilir.
Arayüzü uygulama
Uygulamanızı derlerken Android SDK araçları, .aidl
dosyanızın adını taşıyan bir .java
arayüz dosyası oluşturur. Oluşturulan arayüz, üst arayüzünün soyut bir uygulaması (ör. YourInterface.Stub
) olan Stub
adlı bir alt sınıf içerir ve .aidl
dosyasındaki tüm yöntemleri açıklar.
Not: Stub
birkaç yardımcı yöntem de tanımlar (özellikle asInterface()
). Bu yöntem, genellikle istemcinin onServiceConnected()
geri çağırma yöntemine iletilen IBinder
işlemini alır ve saplama arayüzünün bir örneğini döndürür. Bu yayını oluşturma hakkında daha fazla bilgi için IPC yöntemini ç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 ve anonim bir örnek kullanılarak IRemoteService
adlı arayüzün örnek uygulaması 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. } };
binder
artık hizmetin IPC arayüzünü tanımlayan Stub
sınıfının (Binder
) bir örneğidir. Bir sonraki adımda bu örnek, hizmetle etkileşim kurabilmeleri için müşterilere gösterilir.
AIDL arayüzünüzü uygularken birkaç kurala dikkat edin:
- Gelen çağrıların ana iş parçacığında yürütüleceği garanti edilmez. Bu nedenle, çoklu iş parçacığı işlemeyi en baştan düşünmeniz ve hizmetinizi iş parçacığı açısından güvenli olacak şekilde düzgün bir şekilde oluşturmanız gerekir.
- IPC çağrıları varsayılan olarak eşzamanlıdır. Hizmetin bir isteği tamamlamasının birkaç milisaniyeden uzun sürdüğünü biliyorsanız çağrıyı etkinliğin ana iş parçacığından yapmayın. Uygulama kilitlenebilir ve Android'de "Uygulama Yanıt Vermiyor" iletişim kutusu görüntülenebilir. Bunu istemcideki ayrı bir ileti 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 sunun
Arayüzü hizmetinize uyguladıktan sonra bağlanabilmeleri için arayüzü istemcilerin kullanımına sunmanız gerekir. Hizmetinizin arayüzünü genişletmek için Service
kapsamını genişletin ve önceki bölümde açıklandığı gibi, sınıfınızın oluşturulan Stub
kodunu uygulayan bir örneğini döndürmek amacıyla onBind()
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 bir istemci (ör. bir etkinlik), bu hizmete bağlanmak için bindService()
yöntemini çağırdığında istemcinin onServiceConnected()
geri çağırma işlemi, 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. Bu nedenle, istemci ve hizmet ayrı uygulamalardaysa istemcinin uygulamasının src/
dizininde .aidl
dosyasının bir kopyası bulunmalıdır. Bu kopya android.os.Binder
arayüzünü oluşturarak istemcinin AIDL yöntemlerine erişmesini sağlar.
İstemci onServiceConnected()
geri çağırmasında IBinder
değerini aldığında döndürülen parametreyi YourServiceInterface
türüne yayınlamak için YourServiceInterface.Stub.asInterface(service)
yöntemini ç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'daki
RemoteService.java
sınıfına bakın.
IPC üzerinden nesne geçirme
Android 10'da (API düzeyi 29 veya sonraki sürümler) 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, yeniden pazarlama kodunu ve özel bir sınıfı manuel olarak yazmak için gereken ek iş yükünü önler. Ancak bu, temel bir yapı oluşturur. Özel erişimciler veya başka işlevler isteniyorsa bunun yerine Parcelable
öğesini 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 otomatik olarak left
, top
, right
ve bottom
tam sayı alanlarına sahip bir Java sınıfı oluşturur. İlgili tüm sıralama kodu otomatik olarak uygulanır ve nesne, herhangi bir uygulama eklemek zorunda kalmadan doğrudan kullanılabilir.
Ayrıca, IPC arayüzü aracılığıyla bir işlemden diğerine özel sınıf gönderebilirsiniz. Ancak, sınıfınızın kodunun IPC kanalının diğer tarafında kullanılabildiğinden ve sınıfınızın Parcelable
arayüzünü desteklemesi gerekir. Parcelable
desteği, Android sisteminin nesneleri işlemler genelinde yönetilebilecek temel öğelere ayırmasını sağladığı için önemlidir.
Parcelable
özelliğini destekleyen bir özel 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 alan ve bunu bir
Parcel
öğesine yazanwriteToParcel
işlemini uygulayın. - Sınıfınıza,
Parcelable.Creator
arayüzünü uygulayan nesne olanCREATOR
adlı statik bir alan ekleyin. - Son olarak, aşağıdaki
Rect.aidl
dosyasında gösterildiği gibi, ayrıştırılabilir sınıfınızı tanımlayan bir.aidl
dosyası oluşturun.Özel bir derleme işlemi kullanıyorsanız
.aidl
dosyasını derlemenize eklemeyin. C dilindeki bir üstbilgi dosyasına benzer şekilde, bu.aidl
dosyası derlenmemiştir.
AIDL, nesnelerinizi sıralamak ve ayırmak için oluşturduğu kodda bu yöntemleri ve alanları kullanır.
Örneğin, ayrıştırılabilir bir Rect
sınıfı oluşturmak için aşağıdaki Rect.aidl
dosyasını kullanabilirsiniz:
package android.graphics; // Declare Rect so AIDL can find it and knows that it implements // the parcelable protocol. parcelable Rect;
Şimdi de Rect
sınıfının Parcelable
protokolünü nasıl uyguladığına dair bir örneği görebilirsiniz.
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
öğesine yazabileceğiniz diğer değer türlerini görmek için Parcel
hizmetindeki diğer yöntemlere göz atın.
Uyarı: Diğer işlemlerden veri almanın güvenlik açısından etkilerini unutmayın. Bu durumda, Rect
, Parcel
kapsamındaki dört sayı okur. Ancak bunların, çağrıyı yapanın yapmaya çalıştığı şey için kabul edilebilir değer aralığında olduğundan emin olmak sizin sorumluluğunuzdadır. Uygulamanızı kötü amaçlı yazılımlara karşı nasıl güvende tutacağınızla ilgili daha fazla bilgi için Güvenlik ipuçlarını inceleyin.
Parcelables içeren Bundle bağımsız değişkenlerine sahip yöntemler
Bir yöntem ayrıştırılabilir öğeler içermesi beklenen birBundle
nesnesini kabul ediyorsa Bundle
öğesinden okumayı denemeden önce Bundle.setClassLoader(ClassLoader)
çağırarak Bundle
classloader'ını ayarladığınızdan emin olun. Aksi takdirde, ayrıştırılabilir öğe uygulamanızda doğru bir şekilde tanımlanmış olsa bile ClassNotFoundException
ile 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); }Aşağıdaki uygulamada gösterildiği gibi
ClassLoader
, Rect
okunmadan önce Bundle
içinde açıkça ayarlanmıştı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öntemini çağırma
AIDL ile tanımlanmış uzak bir arayüzü çağırmak için çağrı sınıfınızda aşağıdaki adımları uygulayın:
.aidl
dosyasını projeninsrc/
dizinine ekleyin.- AIDL'ye göre oluşturulan
IBinder
arayüzünün bir örneğini beyan edin. ServiceConnection
uygulamasını uygulayın.ServiceConnection
uygulamanızı geçenContext.bindService()
numaralı telefonu arayın.onServiceConnected()
uygulamanızdaservice
adlı birIBinder
örneği alırsınız. Döndürülen parametreyiYourInterface
türüne yayınlamak içinYourInterfaceName.Stub.asInterface((IBinder)service)
yöntemini çağırın.- Arayüzünüzde tanımladığınız yöntemleri çağırın. Bağlantı kopduğunda atılan
DeadObjectException
istisnalarını her zaman yakala. Ayrıca, IPC yöntem çağrısına dahil olan iki işlem çakışan AIDL tanımlarına sahip olduğunda atılan tuzakSecurityException
istisnalarıdır. - Bağlantıyı kesmek için arayüzünüzün örneğiyle birlikte
Context.unbindService()
yöntemini çağırın.
Bir IPC hizmeti çağırırken şu noktaları aklınızda bulundurun:
- Nesneler işlemler genelinde 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 edinmek için Sınır hizmetlerine genel bakış bölümünü okuyun.
ApiDemos projesindeki Uzak Hizmet örneğinden alınan ve AIDL tarafından oluşturulan bir hizmetin çağrılmasını gösteren örnek kod aşağıda 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); } } } }