Android Arayüz Tanımlama Dili (AIDL)

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 bunu Binder 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:

  1. .aidl dosyasını oluşturun

    Bu dosya, yöntem imzaları içeren programlama arayüzünü tanımlar.

  2. Arayüz uygulama

    Android SDK araçları, JavaScript programlama dilinizde arayüz oluşturur; .aidl dosyası yükleyin. Bu arayüz, genişleyen Stub adlı iç soyut sınıfa sahip Binder ve AIDL arayüzünüzdeki yöntemleri uygular. Stub sınıfını kullanmaya devam edin ve yöntemleri uygulayın.

  3. Arayüzü müşterilere gösterin

    Stub uygulamanızı döndürmek için Service uygulayın ve onBind() 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 dizileri
  • String
  • 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. CEVAP List 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 bir ArrayList 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 bir HashMap, ancak yöntem, Map arayüzünü kullanmak için oluşturulmuştur. Şu özelliklerden faydalanabilirsiniz: Map yerine Bundle 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 veya inout (aşağıdaki örneğe bakın).

    Temel öğeler, String, IBinder ve AIDL tarafından oluşturulmuş arayüzler varsayılan olarak in ş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:

  1. Sınıfınızın Parcelable arayüzünü uygulamasını sağlayın.
  2. writeToParcel uygulayın. Bu da nesnenin o anki durumunu gösterir ve bunu bir Parcel öğesine yazar.
  3. Sınıfınıza, uygulanabilecek CREATOR adlı statik alanı ekleyin Parcelable.Creator arayüzü.
  4. 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 bir Bundle 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);
}
Aşağıdaki uygulamada gösterildiği gibi, 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:

  1. .aidl dosyasını projenin src/ dizinine ekleyin.
  2. IBinder arayüzünün, AIDL'yi tıklayın.
  3. ServiceConnection uygulayın.
  4. Context.bindService() numaralı telefonu arayın, ServiceConnection uygulamanızı iletebilirsiniz.
  5. onServiceConnected() uygulamanızda, IBinder örneği, service. Telefonla arama YourInterfaceName.Stub.asInterface((IBinder)service) - döndürülen parametreyi YourInterface türüne dönüştürün.
  6. 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ılan SecurityException istisnalarını da tuzağa düşürebilirsiniz.
  7. 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&mdash;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&mdash;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);
            }
        }
    }
}