Android Arayüz Tanımlama Dili (AIDL), diğer IDL: programlama arayüzünü tanımlamanızı sağlar. Bu kullanarak birbirleriyle iletişim kurmaları için hususunda anlaşmaya varır. işlemler arası iletişim (IPC)
Android'de normalde bir işlem hatırlamaya çalışın. Konuşmak için nesnelerini, gerçek hayattan alacakları ilkelere ayırmaları gerekir. sizin için bu sınırdaki nesneleri anlayabilir ve sıralayabilir. Kodun çok zahmetli bir çözüm olduğundan Android, bu işi sizin için AIDL ile yapabilir.
Not: AIDL, yalnızca istemcilerin
IPC için hizmetinize farklı uygulamalar erişiyorsa ve birden fazla iş parçacığı oluşturmak istiyorsanız
geliştirmenizi sağlar. Cihazlarda aynı anda IPC kullanmanız gerekmiyorsa
kullanıyorsanız, kullanıcı arabiriminizi uygulamak için
Binder
değerleridir.
IPC gerçekleştirmek istiyorsanız ancak çoklu iş parçacıklarını işlemeniz gerekmiyorsa
arayüzünüzü bir Messenger
kullanarak uygulayın.
Yine de önce bağlı hizmetleri anladığınızdan emin olun.
AIDL'yi uygulamaktı.
AIDL arayüzünüzü tasarlamaya başlamadan önce, bir AIDL arayüzüne yapılan çağrıların doğrudan işlev çağrıları. Aramanın geçtiği mesaj dizisi hakkında varsayımlarda bulunmayın. gerçekleşir. Ne olacağı, aramanın yerel işlem veya uzak bir işlem:
- Yerel işlemden yapılan aramalar, aramayı yapan aynı mesaj dizisinde yürütülür. Eğer
bu iş parçacığı AIDL arayüzünde yürütülmeye devam eder. Eğer
kodunuzu hizmette yürüten iş parçacığıdır. Bu yüzden, yalnızca yerel
iş parçacıkları hizmete eriştiğinde, içinde hangi iş parçacıklarının yürütüldüğünü tamamen kontrol edebilirsiniz. Ama
Bu durumda AIDL kullanmayın; bunun yerine
uygulayarak bir
Binder
değerleridir. - Uzak bir işlemden gelen çağrılar, platformun içinde tuttuğu bir iş parçacığı havuzundan gönderilir ele alacağız. Birden fazla aramayla, bilinmeyen mesaj dizilerinden gelen aramalara hazırlıklı olun aynı anda gerçekleşmesini sağlar. Başka bir deyişle, AIDL arayüzünün uygulanması iş parçacığı açısından güvenli değil. Aynı uzak nesnedeki bir iş parçacığından yapılan çağrılar alıcı tarafında sırayla gelir.
oneway
anahtar kelimesi, uzaktan aramaların davranışını değiştirir. Kullanıldığında, uzaktan çağrı şunları yapar: engellemez. İşlem verilerini gönderip hemen iade eder. Arayüzün uygulanması, sonunda bunuBinder
iş parçacığı havuzundan normal bir uzak çağrı olarak gelen normal bir çağrı olarak alır.oneway
, yerel aramada kullanılıyorsa etkisi yoktur ve görüşme yine de eşzamanlıdır.
AIDL arayüzü tanımlama
Java kullanarak bir .aidl
dosyasında AIDL arayüzünüzü tanımlayın
programlama dili söz dizimini kullanır, sonra her ikisinin de src/
dizinine, kaynak koduna kaydedin
hizmeti barındıran uygulama ve hizmete bağlanan diğer uygulamalar.
.aidl
dosyasını içeren her bir uygulamayı derlediğinizde, Android SDK araçları
.aidl
dosyasına dayalı bir IBinder
arayüzü oluşturup dosyayı
projenin gen/
dizinini oluşturur. Hizmet, IBinder
gerektiği gibi kontrol edin. Böylece istemci uygulamalar,
IPC'yi gerçekleştirmek için IBinder
.
AIDL kullanarak sınırlı hizmet oluşturmak için aşağıdaki adımları uygulayın: aşağıdaki bölümlerde bulabilirsiniz:
.aidl
dosyasını oluşturunBu dosya, yöntem imzaları içeren programlama arayüzünü tanımlar.
- Arayüz uygulama
Android SDK araçları, JavaScript programlama dilinizde arayüz oluşturur;
.aidl
dosyası yükleyin. Bu arayüz, genişleyenStub
adlı iç soyut sınıfa sahipBinder
ve AIDL arayüzünüzdeki yöntemleri uygular.Stub
sınıfını kullanmaya devam edin ve yöntemleri uygulayın. - Arayüzü müşterilere gösterin
Stub
uygulamanızı döndürmek içinService
uygulayın veonBind()
değerini geçersiz kılın sınıfını kullanır.
Dikkat: AIDL arayüzünüzde yapacağınız
Diğer uygulamaların bozulmaması için ilk sürümünüz geriye dönük uyumlu olmalıdır.
hizmet kullanan kişiler de olabilir. Yani, .aidl
dosyanızın diğer uygulamalara kopyalanması gerekeceği için
Hizmetinizin arayüzüne erişebilmelerini istiyorsanız, orijinal
kullanır.
.aidl dosyasını oluşturma
AIDL, bir arayüzü tanımlamak için kullanabileceğiniz bir veya daha fazla yönteme sahip olan basit bir söz dizimi kullanır. ve değer döndürür. Parametreler ve döndürülen değerler herhangi bir türde olabilir. AIDL tarafından oluşturulan arayüzler.
.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 bildirimi ile yöntemini gerektirir
imzalar.
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 temel tür dizileriString
CharSequence
List
List
içindeki tüm öğeler, bu beyan ettiğiniz diğer AIDL tarafından oluşturulmuş arayüz veya ayrıştırıcılardan birini içermelidir. CEVAPList
isteğe bağlı olarak parametre haline getirilmiş tür bir sınıf olarak kullanılabilir. Örneğin:List<String>
. Diğer tarafın aldığı gerçek beton sınıf her zaman birArrayList
olsa da yöntemi,List
arayüzünü kullanmak üzere oluşturulmuştur.Map
Map
içindeki tüm öğeler, bu beyan ettiğiniz diğer AIDL tarafından oluşturulmuş arayüz veya ayrıştırıcılardan birini içermelidir. Parametreli tür eşlemeleri örneğin,Map<String,Integer>
desteklenmiyor. Karşı tarafın kattığı gerçek beton sınıfı. aldığı değer her zaman birHashMap
, ancak yöntem,Map
arayüzünü kullanmak için oluşturulmuştur. Şu özelliklerden faydalanabilirsiniz:Map
yerineBundle
kullanabilirsiniz.
Daha önce listelenmeyen her ek tür için bir import
ifadesi eklemeniz gerekir.
olsalar bile arayüzünüzle aynı pakette tanımlansalar bile.
Hizmet arayüzünüzü tanımlarken aşağıdakilere dikkat edin:
- Yöntemler sıfır veya daha fazla parametre alabilir ve bir değer ya da boşluk döndürebilir.
- Birincil olmayan tüm parametreler, verilerin hangi yöne gittiğini gösteren bir yön etiketi gerektirir:
in
,out
veyainout
(aşağıdaki örneğe bakın).Temel öğeler,
String
,IBinder
ve AIDL tarafından oluşturulmuş arayüzler varsayılan olarakin
şeklindedir ve aksi takdirde kullanılamaz.Dikkat: Yalnızca doğru olan içeriği gerekir, çünkü marshalling parametreleri pahalıdır.
.aidl
dosyasına eklenen tüm kod yorumlarıIBinder
gelir sağladı içe aktarma ve paketten önceki yorumlar hariç arayüz açıklamalarına dikkat edin.- Dize ve int sabitleri, AIDL arayüzünde
const int VERSION = 1;
gibi tanımlanabilir. - Yöntem çağrıları,
transact()
kodu kullanın. Çünkü bu sürüm oluşturmayı zorlaştırırsa, , işlem kodunu manuel olarak şu yönteme atayabilir: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 bir .aidl
dosyası aşağıda 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. Google Takvim widget'ını
uygulamanızı derlerseniz, SDK araçları uygulamanızın IBinder
arayüz dosyasını
projenin gen/
dizini. Oluşturulan dosyanın adı, .aidl
dosyasının adıyla eşleşir ancak
.java
uzantılı. Örneğin, IRemoteService.aidl
IRemoteService.java
ile sonuçlanır.
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ı derleyeceksiniz. gradle assembleDebug
ile projenizi derleyin
veya .aidl
dosyasını yazmayı bitirir bitirmez gradle assembleRelease
,
Böylece, kodunuzu oluşturulan sınıfa bağlayabilirsiniz.
Arayüzü uygulama
Uygulamanızı oluşturduğunuzda Android SDK araçları bir .java
arayüz dosyası oluşturur
.aidl
dosyanızın adını taşır. Oluşturulan arayüz Stub
adlı bir alt sınıf içerir
bu, YourInterface.Stub
gibi üst arayüzünün soyut bir uygulamasıdır ve .aidl
dosyasındaki tüm yöntemleri açıklar.
Not: Stub
ayrıca
birkaç yardımcı yöntem tanımlar. asInterface()
, bu yöntem genellikle istemcinin onServiceConnected()
geri çağırma yöntemine iletilen IBinder
yöntemini alır.
saplama arayüzünün bir örneğini döndürür. Bu yayının nasıl yapılacağı hakkında daha fazla bilgi için IPC'yi çağırma bölümüne bakın
yöntemini kullanın.
.aidl
öğesinden oluşturulan arayüzü uygulamak için, oluşturulan Binder
öğesini genişletin
arayüzü (YourInterface.Stub
gibi) tıklayın ve
.aidl
dosyasından devralındı.
Aşağıda, IRemoteService
adlı arayüzün önceki
Anonim bir örnek kullanılarak IRemoteService.aidl
örneği:
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
, Stub
sınıfının (bir Binder
) bir örneğidir.
Bu kimlik, hizmet için IPC arayüzünü tanımlar. Bir sonraki adımda bu örnek
bu sayede hizmetle etkileşimde bulunmaları mümkün olur.
AIDL arayüzünüzü uygularken birkaç kurala dikkat edin:
- Gelen aramaların ana iş parçacığında yürütüleceği garanti edilmez. Bu nedenle, ve hizmetinizi iş parçacığı açısından güvenli olacak şekilde derleyin.
- Varsayılan olarak IPC çağrıları eşzamanlıdır. Hizmetin birkaç dakikada bir milisaniye cinsinden belirtmek gerekirse, isteği etkinliğin ana iş parçacığından çağırmayın. Uygulamanın kilitlenmesine neden olarak Android'de "Uygulama Yanıt Vermiyor" mesajı görüntüleniyor olabilir iletişim kutusu. İleti dizisini, istemcideki ayrı bir ileti dizisinden çağırın.
- Yalnızca şunun referans dokümanları altında listelenen istisna türleri:
Parcel.writeException()
arayana geri gönderilir.
Arayüzü müşterilere gösterme
Hizmetiniz için arayüzü uyguladıktan sonra,
bağlamayı öğreteceğim. Arayüzü göstermek için
kullanın, şunu uygulayan bir sınıf örneği döndürmek için Service
öğesini genişletin ve onBind()
uygulayın:
önceki bölümde açıklandığı gibi, oluşturulan Stub
. Bir örnekle açıklayalım
IRemoteService
örnek arayüzünü müşterilere sunan bir hizmet.
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 (örneğin, bir etkinlik) bu hizmete bağlanmak için bindService()
yöntemini aradığında, istemcinin onServiceConnected()
geri çağırması
Hizmetin onBind()
tarafından binder
örneği döndürüldü
yöntemidir.
İstemcinin arayüz sınıfına da erişimi olmalıdır. Müşteri ve hizmet
istemcinin uygulamasında .aidl
dosyasının bir kopyası bulunmalıdır.
src/
dizininde android.os.Binder
oluşturur.
AIDL yöntemlerine istemci erişimi sağlar.
Müşteri IBinder
aldığında
onServiceConnected()
geri çağırmasında
Döndürülen oyuncuyu yayınlamak için YourServiceInterface.Stub.asInterface(service)
parametresini YourServiceInterface
türüne ayarlayın:
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
RemoteService.java
sınıf
ApiDemos hakkında daha fazla bilgi edinin.
IPC üzerinden nesne iletme
Android 10'da (API düzeyi 29 veya sonraki sürümler)
Parcelable
nesne doğrudan
AIDL'yi tıklayın. 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, manuel olarak marshalling kodunu ve özel bir kodu manuel olarak yazmak için ek işin
sınıfını kullanır. Ancak bu aynı zamanda temel bir yapı oluşturur. Özel erişimciler veya diğer işlevler
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 otomatik olarak left
tam sayı alanlarına sahip bir Java sınıfı oluşturur.
top
, right
ve bottom
. İlgili tüm marshalling kodları
uygulanır ve nesne, herhangi bir ek dosya eklenmesine gerek kalmadan doğrudan kullanılabilir.
hakkında bilgi edindiniz.
Ayrıca, IPC arayüzü aracılığıyla bir işlemden diğerine özel bir sınıf gönderebilirsiniz. Ancak,
sınıfınızın kodunun IPC kanalının diğer tarafında bulunduğundan emin olun ve
sınıfınız Parcelable
arayüzünü desteklemelidir. Destekleyici
Parcelable
önemlidir
Çünkü Android sisteminin nesneleri sıralanabilecek temel öğelere ayırmasına olanak tanır.
farklı süreçlerde
değer yaratır.
Parcelable
destekleyen özel bir sınıf oluşturmak için:
takip etmek için:
- Sınıfınızın
Parcelable
arayüzünü uygulamasını sağlayın. writeToParcel
uygulayın. Bu da nesnenin o anki durumunu gösterir ve bunu birParcel
öğesine yazar.- Sınıfınıza, uygulanabilecek
CREATOR
adlı statik alanı ekleyinParcelable.Creator
arayüzü. - Son olarak, aşağıdaki için gösterildiği gibi ayrıştırılabilir sınıfınızı tanımlayan bir
.aidl
dosyası oluşturun.Rect.aidl
dosyaÖzel bir derleme işlemi kullanıyorsanız
.aidl
dosyasını ana makinenize eklemeyin seçeceğiz. C dilindeki başlık dosyasına benzer şekilde, bu.aidl
dosyası derlenmemiştir.
AIDL, oluşturduğu kodda bu yöntemleri ve alanları marshall ve unmarshall için kullanır izin verir.
Örneğin, aşağıdaki Rect.aidl
dosyası gibi bir Rect
sınıfı oluşturabilirsiniz:
ayrıştırılabilir:
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ü.
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 oldukça basittir. Diğerlerine göz atın
Parcel
üzerinden, yazabileceğiniz diğer değer türlerini görmek için
Parcel
olarak değiştirdik.
Uyarı: E-posta almanın güvenlik sonuçlarını hatırla
diğer işlemlerdeki verilerden
yararlanmanız gerekir. Bu durumda, Rect
, Parcel
üzerinden dört rakam okur, ancak bunların kabul edilebilir aralık dahilinde olduklarından emin olmak size bağlıdır
bir değer verdiğinizi gösterin. Uygulamanızı kötü amaçlı yazılımlara karşı nasıl koruyacağınız hakkında daha fazla bilgi edinmek için Güvenlik ipuçları'na bakın.
Parcelables içeren Bundle bağımsız değişkenlerine sahip yöntemler
Bir yöntem içermesi beklenen birBundle
nesnesini kabul ediyorsa
ayrıştırılabilir hale getirmek istiyorsanız, Bundle
öğesinin sınıf yükleyicisini
okumaya çalışmadan önce Bundle.setClassLoader(ClassLoader)
aranıyor
Bundle
arasında. Aksi takdirde, ayrıştırılabilir öğe uygulamanızda doğru şekilde tanımlanmış olsa bile ClassNotFoundException
ile karşılaşırsınız.
Örneğin, aşağıdaki örnek .aidl
dosyasını göz önünde bulundurun:
// 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); }
ClassLoader
Rect
okumadan önce Bundle
içinde açıkça ayarlanmış:
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 aşağıdaki adımları uygulayın: görüşme sınıfınız:
.aidl
dosyasını projeninsrc/
dizinine ekleyin.IBinder
arayüzünün, AIDL'yi tıklayın.ServiceConnection
uygulayın.Context.bindService()
numaralı telefonu arayın,ServiceConnection
uygulamanızı iletebilirsiniz.onServiceConnected()
uygulamanızda,IBinder
örneği,service
. Telefonla aramaYourInterfaceName.Stub.asInterface((IBinder)service)
- döndürülen parametreyiYourInterface
türüne dönüştürün.- Arayüzünüzde tanımladığınız yöntemleri çağırın. Her zaman yakalayın
DeadObjectException
istisna bağlantısı kopar. Ayrıca, IPC yöntem çağrısına dahil olan iki işlem çelişen AIDL tanımlarına sahip olduğunda atılanSecurityException
istisnalarını da tuzağa düşürebilirsiniz. - Bağlantıyı kesmek için arayüzünüzün örneğiyle birlikte
Context.unbindService()
yöntemini çağırın.
Bir IPC hizmetini çağırırken aşağıdaki noktaları aklınızda bulundurun:
- Nesneler, işlemlerde referans olarak sayılır.
- Anonim nesneler gönderebilirsiniz. yöntem bağımsız değişkenleri olarak kullanılır.
Bir hizmete bağlama hakkında daha fazla bilgi için Sınır hizmetlere genel bakış'ı okuyun.
Aşağıda, AIDL tarafından oluşturulmuş bir hizmeti çağırmayı gösteren örnek bir kod verilmiştir: Uzak Hizmet örneğindeki ApiDemos projesinden.
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); } } } }