Ringkasan layanan terikat

Layanan terikat adalah server di antarmuka klien-server. Layanan ini memungkinkan komponen seperti aktivitas mengikat ke layanan, mengirim permintaan, menerima respons, dan melakukan komunikasi antar-proses (IPC). Layanan terikat biasanya aktif hanya saat melayani komponen aplikasi lain dan tidak berjalan di latar belakang terus-menerus.

Dokumen ini menjelaskan cara membuat layanan terikat, termasuk cara mengikat ke layanan dari komponen aplikasi lain. Untuk informasi tambahan tentang layanan secara umum, seperti cara mengirimkan notifikasi dari layanan dan menyetel layanan agar berjalan di latar depan, lihat Ringkasan layanan.

Dasar-dasar

Layanan terikat adalah implementasi class Service yang memungkinkan aplikasi lain terikat dan berinteraksi dengannya. Untuk menyediakan binding bagi layanan, Anda harus mengimplementasikan metode callback onBind(). Metode ini menampilkan objek IBinder yang mendefinisikan antarmuka pemrograman yang dapat digunakan klien untuk berinteraksi dengan layanan.

Mengikat ke layanan yang dimulai

Seperti yang dibahas dalam Ringkasan layanan, Anda dapat membuat layanan yang dimulai dan terikat. Artinya, Anda dapat memulai layanan dengan memanggil startService(), yang memungkinkan layanan berjalan tanpa batas waktu. Anda juga dapat mengizinkan klien mengikat ke layanan dengan memanggil bindService().

Jika Anda mengizinkan layanan dimulai dan terikat, saat layanan dimulai, sistem tidak akan menghancurkan layanan saat semua klien melepaskan ikatan. Sebaliknya, Anda harus menghentikan layanan secara eksplisit, dengan memanggil stopSelf() atau stopService().

Meskipun biasanya Anda harus mengimplementasikan onBind() atau onStartCommand(), terkadang perlu untuk mengimplementasikan keduanya. Misalnya, pemutar musik mungkin merasa berguna untuk membiarkan layanannya berjalan tanpa batas waktu dan juga menyediakan binding. Dengan cara ini, aktivitas dapat memulai layanan untuk memutar musik, dan musik akan terus diputar meskipun pengguna keluar dari aplikasi. Lalu, saat pengguna kembali ke aplikasi, aktivitas dapat mengikat ke layanan untuk mendapatkan kembali kontrol pemutaran.

Untuk mengetahui informasi selengkapnya tentang siklus proses layanan saat menambahkan binding ke layanan yang dimulai, lihat bagian Mengelola siklus proses layanan terikat.

Klien mengikat ke layanan dengan memanggil bindService(). Bila itu dilakukan, klien harus menyediakan implementasi ServiceConnection, yang akan memantau koneksi dengan layanan. Nilai hasil bindService() menunjukkan apakah layanan yang diminta ada dan apakah klien diziinkan mengaksesnya.

Saat sistem Android membuat koneksi antara klien dan layanan, sistem akan memanggil onServiceConnected() pada ServiceConnection. Metode onServiceConnected() menyertakan argumen IBinder, yang kemudian digunakan klien untuk berkomunikasi dengan layanan terikat.

Anda dapat menghubungkan beberapa klien ke layanan secara bersamaan. Namun, sistem melakukan cache pada saluran komunikasi layanan IBinder. Dengan kata lain, sistem akan memanggil metode onBind() layanan untuk membuat IBinder hanya saat klien pertama terikat. Sistem kemudian memberikan IBinder yang sama ke semua klien tambahan yang mengikat ke layanan yang sama, tanpa memanggil onBind() lagi.

Saat klien terakhir melepaskan ikatan dari layanan, sistem akan menghancurkan layanan, kecuali jika layanan dimulai menggunakan startService().

Bagian terpenting dari implementasi layanan terikat Anda adalah mendefinisikan antarmuka yang dikembalikan metode callback onBind() Anda. Bagian berikut membahas beberapa cara untuk menentukan antarmuka IBinder layanan Anda.

Membuat layanan terikat

Saat membuat layanan yang menyediakan pengikatan, Anda harus menyediakan IBinder yang menyediakan antarmuka pemrograman yang dapat digunakan klien untuk berinteraksi dengan layanan. Ada tiga cara untuk menentukan antarmuka:

Memperluas class Binder
Jika layanan Anda bersifat pribadi untuk aplikasi Anda sendiri dan berjalan dalam proses yang sama dengan klien, yang merupakan hal umum, buat antarmuka Anda dengan memperluas class Binder dan menampilkan instance-nya dari onBind(). Klien akan menerima Binder dan dapat menggunakannya untuk mengakses langsung metode publik yang tersedia dalam implementasi Binder atau Service.

Inilah teknik yang lebih disukai bila layanan Anda sekadar pekerja latar belakang untuk aplikasi Anda sendiri. Satu-satunya kasus penggunaan saat ini bukan cara yang lebih disukai untuk membuat antarmuka adalah jika layanan Anda digunakan oleh aplikasi lain atau di seluruh proses yang terpisah.

Menggunakan Messenger
Jika antarmuka Anda perlu bekerja lintas proses yang berbeda, Anda dapat membuat antarmuka untuk layanan dengan Messenger. Dengan cara ini, layanan mendefinisikan Handler yang akan merespons berbagai tipe objek Message.

Handler ini menjadi dasar untuk Messenger yang kemudian dapat membagikan IBinder dengan klien, sehingga klien dapat mengirim perintah ke layanan menggunakan objek Message. Selain itu, klien dapat menentukan Messenger-nya sendiri, sehingga layanan dapat mengirim pesan kembali.

Inilah cara termudah melakukan komunikasi antarproses (IPC), karena Messenger akan membuat antrean semua permintaan ke dalam satu thread, sehingga Anda tidak perlu mendesain layanan agar thread-safe.

Menggunakan AIDL
Android Interface Definition Language (AIDL) menguraikan objek menjadi primitif yang dapat dipahami oleh sistem operasi dan mengaturnya di seluruh proses untuk melakukan IPC. Teknik sebelumnya, yang menggunakan Messenger, sebenarnya didasarkan pada AIDL yang menjadi struktur dasarnya.

Seperti yang disebutkan di bagian sebelumnya, Messenger membuat antrean semua permintaan klien dalam satu thread, sehingga layanan menerima permintaan satu per satu. Namun, jika ingin layanan Anda menangani beberapa permintaan sekaligus, Anda dapat menggunakan AIDL secara langsung. Dalam hal ini, layanan Anda harus aman untuk thread dan mampu melakukan multithreading.

Untuk menggunakan AIDL secara langsung, buat file .aidl yang menentukan antarmuka pemrograman. Android SDK Tools menggunakan file ini untuk menghasilkan class abstrak yang mengimplementasikan antarmuka dan menangani IPC, yang nantinya dapat Anda perluas dalam layanan.

Catatan: Untuk sebagian besar aplikasi, AIDL bukanlah pilihan terbaik untuk membuat layanan terikat, karena mungkin memerlukan kemampuan multithreading dan dapat menghasilkan implementasi yang lebih rumit. Oleh karena itu, dokumen ini tidak membahas cara menggunakannya untuk layanan Anda. Jika Anda yakin perlu menggunakan AIDL secara langsung, lihat dokumen AIDL.

Memperluas class Binder

Jika hanya aplikasi lokal yang menggunakan layanan Anda dan tidak perlu bekerja di seluruh proses, Anda dapat mengimplementasikan class Binder sendiri yang memberi klien akses langsung ke metode publik dalam layanan.

Catatan: Hal ini hanya berhasil jika klien dan layanan berada dalam aplikasi dan proses yang sama, suatu kondisi yang paling umum. Misalnya, hal ini berfungsi dengan baik untuk aplikasi musik yang perlu mengikat aktivitas ke layanannya sendiri yang memutar musik di latar belakang.

Berikut cara menyiapkannya

  1. Dalam layanan Anda, buat sebuah instance Binder yang melakukan salah satu aktivitas berikut:
    • Menampung metode publik yang dapat dipanggil klien.
    • Mengembalikan instance Service saat ini, yang memiliki metode publik yang dapat dipanggil klien.
    • Mengembalikan instance class lain yang di-host di layanan dengan metode publik yang dapat dipanggil klien.
  2. Mengembalikan instance Binder ini dari metode callback onBind().
  3. Di klien, terima Binder dari metode callback onServiceConnected(), lalu buat panggilan ke layanan terikat menggunakan metode yang disediakan.

Catatan: Layanan dan klien harus berada di aplikasi yang sama sehingga klien dapat mentransmisikan objek yang dikembalikan dan memanggil API-nya dengan benar. Layanan dan klien juga harus berada dalam proses yang sama, karena teknik ini tidak melakukan marshalling apa pun di seluruh proses.

Misalnya, berikut adalah layanan yang memberi klien akses ke metode dalam layanan melalui implementasi Binder:

Kotlin

class LocalService : Service() {
    // Binder given to clients.
    private val binder = LocalBinder()

    // Random number generator.
    private val mGenerator = Random()

    /** Method for clients.  */
    val randomNumber: Int
        get() = mGenerator.nextInt(100)

    /**
     * Class used for the client Binder. Because we know this service always
     * runs in the same process as its clients, we don't need to deal with IPC.
     */
    inner class LocalBinder : Binder() {
        // Return this instance of LocalService so clients can call public methods.
        fun getService(): LocalService = this@LocalService
    }

    override fun onBind(intent: Intent): IBinder {
        return binder
    }
}

Java

public class LocalService extends Service {
    // Binder given to clients.
    private final IBinder binder = new LocalBinder();
    // Random number generator.
    private final Random mGenerator = new Random();

    /**
     * Class used for the client Binder.  Because we know this service always
     * runs in the same process as its clients, we don't need to deal with IPC.
     */
    public class LocalBinder extends Binder {
        LocalService getService() {
            // Return this instance of LocalService so clients can call public methods.
            return LocalService.this;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }

    /** Method for clients. */
    public int getRandomNumber() {
      return mGenerator.nextInt(100);
    }
}

LocalBinder menyediakan metode getService() bagi klien untuk mengambil instance LocalService saat ini. Hal ini memungkinkan klien memanggil metode publik dalam layanan. Misalnya, klien dapat memanggil getRandomNumber() dari layanan.

Berikut adalah aktivitas yang mengikat ke LocalService dan memanggil getRandomNumber() bila tombol diklik:

Kotlin

class BindingActivity : Activity() {
    private var mService: LocalService? = null
    private var mBound: Boolean = false

    /** Defines callbacks for service binding, passed to bindService().  */
    private val connection = object : ServiceConnection {

        override fun onServiceConnected(className: ComponentName, service: IBinder) {
            // We've bound to LocalService, cast the IBinder and get LocalService instance.
            val binder = service as LocalService.LocalBinder
            mService = binder.getService()
            mBound = true
        }

        override fun onServiceDisconnected(arg0: ComponentName) {
            mService = null
            mBound = false
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main)
    }

    override fun onStart() {
        super.onStart()
        // Bind to LocalService.
        Intent(this, LocalService::class.java).also { intent ->
            bindService(intent, connection, Context.BIND_AUTO_CREATE)
        }
    }

    override fun onStop() {
        super.onStop()
        unbindService(connection)
        // Release the reference so the service can be garbage-collected once it's
        // destroyed. onServiceDisconnected() isn't called when the client unbinds.
        mService = null
        mBound = false
    }

    /** Called when a button is clicked (the button in the layout file attaches to
     * this method with the android:onClick attribute).  */
    fun onButtonClick(v: View) {
        if (mBound) {
            // Call a method from the LocalService.
            // However, if this call is something that might hang, then put this request
            // in a separate thread to avoid slowing down the activity performance.
            val num: Int = mService?.randomNumber ?: return
            Toast.makeText(this, "number: $num", Toast.LENGTH_SHORT).show()
        }
    }
}

Java

public class BindingActivity extends Activity {
    LocalService mService;
    boolean mBound = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

    @Override
    protected void onStart() {
        super.onStart();
        // Bind to LocalService.
        Intent intent = new Intent(this, LocalService.class);
        bindService(intent, connection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        unbindService(connection);
        // Release the reference so the service can be garbage-collected once it's
        // destroyed. onServiceDisconnected() isn't called when the client unbinds.
        mService = null;
        mBound = false;
    }

    /** Called when a button is clicked (the button in the layout file attaches to
      * this method with the android:onClick attribute). */
    public void onButtonClick(View v) {
        if (mBound) {
            // Call a method from the LocalService.
            // However, if this call is something that might hang, then put this request
            // in a separate thread to avoid slowing down the activity performance.
            int num = mService.getRandomNumber();
            Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
        }
    }

    /** Defines callbacks for service binding, passed to bindService(). */
    private ServiceConnection connection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName className,
                IBinder service) {
            // We've bound to LocalService, cast the IBinder and get LocalService instance.
            LocalBinder binder = (LocalBinder) service;
            mService = binder.getService();
            mBound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            mService = null;
            mBound = false;
        }
    };
}

Contoh sebelumnya menunjukkan cara klien mengikat ke layanan menggunakan implementasi ServiceConnection dan callback onServiceConnected(). Bagian berikut menyediakan informasi lebih lanjut tentang proses pengikatan ke layanan.

Catatan: Pada contoh sebelumnya, metode onStop() melepaskan ikatan klien dari layanan dan merilis referensi klien ke layanan tersebut. Lepaskan ikatan klien dari layanan pada waktu yang tepat, seperti yang dibahas di bagian Catatan tambahan.

Untuk kode contoh selengkapnya, lihat class LocalService.java dan class LocalServiceActivities.java dalam ApiDemos.

Menggunakan Messenger

Jika layanan perlu berkomunikasi dengan proses jauh, Anda dapat menggunakan Messenger untuk menyediakan antarmuka bagi layanan Anda. Teknik ini memungkinkan Anda melakukan komunikasi antar-proses (IPC) tanpa perlu menggunakan AIDL.

Menggunakan Messenger untuk antarmuka Anda lebih mudah dibandingkan menggunakan AIDL karena Messenger membuat antrean semua panggilan ke layanan. Antarmuka AIDL murni mengirimkan permintaan simultan ke layanan, yang kemudian harus menangani multithreading.

Untuk sebagian besar aplikasi, layanan tidak perlu melakukan multithreading, sehingga penggunaan Messenger memungkinkan layanan menangani satu panggilan dalam satu waktu. Jika layanan Anda harus multithread, gunakan AIDL untuk menentukan antarmuka.

Berikut adalah ringkasan cara menggunakan Messenger:

  1. Layanan mengimplementasikan Handler yang menerima callback untuk setiap panggilan dari klien.
  2. Layanan menggunakan Handler untuk membuat objek Messenger (yang merupakan acuan ke Handler).
  3. Messenger membuat IBinder yang dikembalikan layanan ke klien dari onBind().
  4. Klien menggunakan IBinder untuk membuat instance Messenger (yang mengacu Handler layanan), yang digunakan klien untuk mengirim objek Message ke layanan.
  5. Layanan menerima setiap Message dalam Handler secara spesifik, dalam metode handleMessage().

Dengan cara ini, tidak ada metode untuk dipanggil klien pada layanan. Sebagai gantinya, klien mengirim pesan (objek Message) yang diterima layanan dalam Handler-nya.

Berikut adalah contoh layanan yang menggunakan antarmuka Messenger:

Kotlin

/** Command to the service to display a message.  */
private const val MSG_SAY_HELLO = 1

class MessengerService : Service() {

    /**
     * Target we publish for clients to send messages to IncomingHandler.
     */
    private lateinit var mMessenger: Messenger

    /**
     * Handler of incoming messages from clients.
     */
    internal class IncomingHandler(
            context: Context,
            private val applicationContext: Context = context.applicationContext
    ) : Handler() {
        override fun handleMessage(msg: Message) {
            when (msg.what) {
                MSG_SAY_HELLO ->
                    Toast.makeText(applicationContext, "hello!", Toast.LENGTH_SHORT).show()
                else -> super.handleMessage(msg)
            }
        }
    }

    /**
     * When binding to the service, we return an interface to our messenger
     * for sending messages to the service.
     */
    override fun onBind(intent: Intent): IBinder? {
        Toast.makeText(applicationContext, "binding", Toast.LENGTH_SHORT).show()
        mMessenger = Messenger(IncomingHandler(this))
        return mMessenger.binder
    }
}

Java

public class MessengerService extends Service {
    /**
     * Command to the service to display a message.
     */
    static final int MSG_SAY_HELLO = 1;

    /**
     * Handler of incoming messages from clients.
     */
    static class IncomingHandler extends Handler {
        private Context applicationContext;

        IncomingHandler(Context context) {
            applicationContext = context.getApplicationContext();
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_SAY_HELLO:
                    Toast.makeText(applicationContext, "hello!", Toast.LENGTH_SHORT).show();
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    /**
     * Target we publish for clients to send messages to IncomingHandler.
     */
    Messenger mMessenger;

    /**
     * When binding to the service, we return an interface to our messenger
     * for sending messages to the service.
     */
    @Override
    public IBinder onBind(Intent intent) {
        Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
        mMessenger = new Messenger(new IncomingHandler(this));
        return mMessenger.getBinder();
    }
}

Metode handleMessage() di Handler adalah tempat layanan menerima Message yang masuk dan memutuskan tindakan yang harus dilakukan, berdasarkan anggota what.

Klien tinggal membuat Messenger berdasarkan IBinder yang dikembalikan oleh layanan dan mengirim pesan menggunakan send(). Misalnya, berikut adalah aktivitas yang mengikat ke layanan dan mengirimkan pesan MSG_SAY_HELLO ke layanan:

Kotlin

class ActivityMessenger : Activity() {
    /** Messenger for communicating with the service.  */
    private var mService: Messenger? = null

    /** Flag indicating whether we have called bind on the service.  */
    private var bound: 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 has been
            // established, giving us the object we can use to
            // interact with the service.  We are communicating with the
            // service using a Messenger, so here we get a client-side
            // representation of that from the raw IBinder object.
            mService = Messenger(service)
            bound = true
        }

        override fun onServiceDisconnected(className: ComponentName) {
            // This is called when the connection with the service has been
            // unexpectedly disconnected—that is, its process crashed.
            mService = null
            bound = false
        }
    }

    fun sayHello(v: View) {
        if (!bound) return
        // Create and send a message to the service, using a supported 'what' value.
        val msg: Message = Message.obtain(null, MSG_SAY_HELLO, 0, 0)
        try {
            mService?.send(msg)
        } catch (e: RemoteException) {
            e.printStackTrace()
        }

    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main)
    }

    override fun onStart() {
        super.onStart()
        // Bind to the service.
        Intent(this, MessengerService::class.java).also { intent ->
            bindService(intent, mConnection, Context.BIND_AUTO_CREATE)
        }
    }

    override fun onStop() {
        super.onStop()
        // Unbind from the service.
        if (bound) {
            unbindService(mConnection)
            mService = null
            bound = false
        }
    }
}

Java

public class ActivityMessenger extends Activity {
    /** Messenger for communicating with the service. */
    Messenger mService = null;

    /** Flag indicating whether we have called bind on the service. */
    boolean bound;

    /**
     * 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 has been
            // established, giving us the object we can use to
            // interact with the service.  We are communicating with the
            // service using a Messenger, so here we get a client-side
            // representation of that from the raw IBinder object.
            mService = new Messenger(service);
            bound = true;
        }

        public void onServiceDisconnected(ComponentName className) {
            // This is called when the connection with the service has been
            // unexpectedly disconnected—that is, its process crashed.
            mService = null;
            bound = false;
        }
    };

    public void sayHello(View v) {
        if (!bound) return;
        // Create and send a message to the service, using a supported 'what' value.
        Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
        try {
            mService.send(msg);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

    @Override
    protected void onStart() {
        super.onStart();
        // Bind to the service.
        bindService(new Intent(this, MessengerService.class), mConnection,
            Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        // Unbind from the service.
        if (bound) {
            unbindService(mConnection);
            mService = null;
            bound = false;
        }
    }
}

Contoh ini tidak menunjukkan cara layanan dapat merespons klien. Jika ingin layanan merespons, Anda juga perlu membuat Messenger di klien. Saat klien menerima callback onServiceConnected(), klien akan mengirim Message ke layanan yang berisi Messenger klien dalam parameter replyTo metode send().

Anda dapat melihat contoh cara menyediakan fitur pesan dua arah dalam contoh MessengerService.java (layanan) dan MessengerServiceActivities.java (klien).

Mengikat ke layanan

Komponen aplikasi (klien) dapat mengikat ke layanan dengan memanggil bindService(). Sistem Android kemudian memanggil onBind() layanan, yang mengembalikan IBinder untuk berinteraksi dengan layanan.

Pengikatan tersebut asinkron, dan bindService() segera kembali tanpa mengembalikan IBinder ke klien. Untuk menerima IBinder, klien harus membuat instance ServiceConnection dan meneruskannya ke bindService(). ServiceConnection berisi metode callback yang dipanggil sistem untuk mengirim IBinder.

Catatan: Hanya aktivitas, layanan, dan penyedia konten yang dapat mengikat ke layanan, yang tidak dapat Anda ikat ke layanan dari penerima siaran.

Jadi, untuk mengikat layanan dari klien, Anda harus:

  1. Mengimplementasikan ServiceConnection.

    Implementasi Anda harus mengganti dua metode callback:

    onServiceConnected()
    Sistem memanggil ini untuk mengirim IBinder yang dikembalikan oleh metode onBind() layanan.
    onServiceDisconnected()
    Sistem Android memanggil ini saat koneksi ke layanan tiba-tiba terputus, seperti saat layanan mengalami error atau dihentikan. Ini tidak dipanggil saat klien melepas ikatan.
  2. Panggil bindService(), dengan meneruskan implementasi ServiceConnection.

    Catatan: Jika metode ini mengembalikan false, berarti klien Anda tidak memiliki koneksi yang valid ke layanan. Namun, panggil unbindService() di klien Anda. Jika tidak, klien Anda akan mencegah layanan berhenti saat tidak ada aktivitas.

  3. Bila sistem memanggil metode callback onServiceConnected(), Anda dapat mulai membuat panggilan ke layanan, menggunakan metode yang ditetapkan oleh antarmuka.
  4. Untuk memutus koneksi dari layanan, panggil unbindService().

    Jika klien Anda tetap terikat ke layanan saat aplikasi Anda menghapus klien, penghapusan tersebut akan menyebabkan ikatan klien terputus. Praktik yang lebih baik adalah melepaskan ikatan klien segera setelah selesai berinteraksi dengan layanan. Dengan demikian, layanan yang tidak ada aktivitas akan dinonaktifkan. Untuk mengetahui informasi selengkapnya tentang waktu yang tepat untuk mengikat dan melepas ikatan, lihat bagian Catatan tambahan.

Contoh berikut menghubungkan klien ke layanan yang dibuat sebelumnya dengan memperluas class Binder, sehingga yang perlu dilakukan hanyalah mentransmisikan IBinder yang ditampilkan ke class LocalBinder dan meminta instance LocalService:

Kotlin

var mService: LocalService? = null

val mConnection = object : ServiceConnection {
    // Called when the connection with the service is established.
    override fun onServiceConnected(className: ComponentName, service: IBinder) {
        // Because we have bound to an explicit
        // service that is running in our own process, we can
        // cast its IBinder to a concrete class and directly access it.
        val binder = service as LocalService.LocalBinder
        mService = binder.getService()
        mBound = true
    }

    // Called when the connection with the service disconnects unexpectedly.
    override fun onServiceDisconnected(className: ComponentName) {
        Log.e(TAG, "onServiceDisconnected")
        mService = null
        mBound = false
    }
}

Java

LocalService mService;
private ServiceConnection mConnection = new ServiceConnection() {
    // Called when the connection with the service is established.
    public void onServiceConnected(ComponentName className, IBinder service) {
        // Because we have bound to an explicit
        // service that is running in our own process, we can
        // cast its IBinder to a concrete class and directly access it.
        LocalBinder binder = (LocalBinder) service;
        mService = binder.getService();
        mBound = true;
    }

    // Called when the connection with the service disconnects unexpectedly.
    public void onServiceDisconnected(ComponentName className) {
        Log.e(TAG, "onServiceDisconnected");
        mService = null;
        mBound = false;
    }
};

Dengan ServiceConnection ini, klien bisa mengikat ke layanan dengan meneruskannya ke bindService(), seperti yang ditampilkan pada contoh berikut:

Kotlin

Intent(this, LocalService::class.java).also { intent ->
    bindService(intent, connection, Context.BIND_AUTO_CREATE)
}

Java

Intent intent = new Intent(this, LocalService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
  • Parameter pertama bindService() adalah Intent yang secara eksplisit menyebutkan layanan yang akan diikat.

    Perhatian: Jika Anda menggunakan intent untuk mengikat ke Service, pastikan aplikasi Anda aman dengan menggunakan intent eksplisit. Menggunakan intent implisit untuk memulai layanan dapat membahayakan keamanan karena Anda tidak dapat memastikan layanan yang akan merespons intent tersebut, dan pengguna tidak dapat melihat layanan yang dimulai. Mulai dari Android 5.0 (API level 21), sistem akan melontarkan pengecualian jika Anda memanggil bindService() dengan intent implisit.

  • Parameter kedua adalah objek ServiceConnection.
  • Parameter ketiga adalah flag yang menunjukkan opsi untuk binding—biasanya BIND_AUTO_CREATE, untuk membuat layanan jika belum aktif. Nilai lainnya yang mungkin digunakan adalah BIND_DEBUG_UNBIND, BIND_NOT_FOREGROUND, atau 0 untuk tidak ada.

Catatan tambahan

Berikut ini beberapa catatan penting tentang pengikatan ke layanan:

  • Selalu tangkap pengecualian DeadObjectException, yang ditampilkan saat koneksi terputus. Inilah satu-satunya pengecualian yang dilontarkan oleh metode jauh.
  • Objek adalah acuan yang dihitung lintas proses.
  • Anda biasanya menyambungkan dan melepaskan binding selama momen pengaktifan dan penonaktifan yang cocok dari siklus proses klien, seperti yang dijelaskan dalam contoh berikut:
    • Jika Anda perlu berinteraksi dengan layanan hanya saat aktivitas terlihat, ikat selama onStart() dan lepaskan ikatan selama onStop().
    • Jika Anda ingin aktivitas menerima respons bahkan saat dihentikan di latar belakang, ikat selama onCreate() dan lepaskan ikatan selama onDestroy(). Perlu diketahui bahwa hal ini berarti aktivitas Anda perlu menggunakan layanan selama layanan berjalan, bahkan di latar belakang, sehingga saat layanan berada dalam proses lain, Anda akan meningkatkan bobot proses dan kemungkinan besar akan dihentikan oleh sistem.

    Catatan: Anda biasanya tidak mengikat dan melepaskan ikatan selama callback onResume() dan onPause() aktivitas, karena callback ini terjadi di setiap transisi siklus proses. Pertahankan pemrosesan yang terjadi pada transisi ini seminimal mungkin.

    Selain itu, jika beberapa aktivitas dalam aplikasi Anda terikat ke layanan yang sama dan ada transisi antara dua aktivitas tersebut, layanan mungkin akan dihancurkan dan dibuat ulang saat aktivitas saat ini melepaskan ikatan (selama jeda) sebelum aktivitas berikutnya terikat (selama melanjutkan). Transisi aktivitas untuk cara aktivitas mengoordinasikan siklus prosesnya dijelaskan dalam Siklus proses aktivitas.

Untuk mengetahui kode contoh lainnya yang menunjukkan cara mengikat ke layanan, lihat class RemoteService.java di ApiDemos.

Mengelola siklus proses layanan terikat

Jika layanan tidak terikat dari semua klien, sistem Android akan menghancurkannya (kecuali jika layanan dimulai menggunakan startService()). Jadi, Anda tidak perlu mengelola siklus proses layanan jika layanan tersebut murni merupakan layanan terikat. Sistem Android mengelolanya untuk Anda berdasarkan apakah layanan terikat ke klien mana pun.

Namun, jika Anda memilih untuk mengimplementasikan metode callback onStartCommand(), Anda harus menghentikan layanan secara eksplisit, karena layanan kini dianggap dimulai. Dalam hal ini, layanan akan berjalan hingga menghentikan dirinya sendiri dengan stopSelf() atau komponen lain memanggil stopService(), terlepas dari apakah layanan terikat ke klien atau tidak.

Selain itu, jika layanan Anda dimulai dan menerima binding, saat sistem memanggil metode onUnbind(), Anda dapat secara opsional menampilkan true jika ingin menerima panggilan ke onRebind() saat klien mengikat ke layanan pada waktu berikutnya. onRebind() tidak mengembalikan apa-apa, namun klien masih menerima IBinder dalam callback onServiceConnected(). Gambar di bawah ini mengilustrasikan logika untuk jenis siklus proses ini.

Siklus proses layanan yang dimulai dan terikat: onStartCommand() dan onBind() dipanggil; saat semua klien melepaskan ikatan, onUnbind() dipanggil; pada pengikatan berikutnya, onRebind() dipanggil jika onUnbind() menampilkan true, jika tidak, onBind(); setelah layanan dihentikan, onDestroy() dipanggil.
Gambar 1. Siklus proses untuk layanan yang dimulai dan juga memungkinkan binding.

Untuk mengetahui informasi selengkapnya tentang siklus proses layanan yang dimulai, lihat Ringkasan layanan.