バインドされたサービスの概要

バインドされたサービスは、クライアント サーバー インターフェースにあるサーバーです。アクティビティなどのコンポーネントをサービスにバインドし、リクエストの送信、レスポンスの受信、プロセス間通信(IPC)の実行を行うことができます。通常、バインドされたサービスは他のアプリ コンポーネントにサービスを提供している間だけ機能し、バックグラウンドで無期限に動作することはありません。

このドキュメントでは、バインドされたサービスの作成方法や、他のアプリ コンポーネントからサービスにバインドする方法について説明します。サービスから通知を配信する方法や、サービスをフォアグラウンドで実行するように設定する方法など、サービス全般の詳細については、 サービスの概要をご覧ください。

基本情報

バインドされたサービスは、他のアプリケーションがサービスにバインドしてサービスを操作できるようにする Service クラスの実装です。サービスにバインディングを提供するには、onBind() コールバック メソッドを実装します。このメソッドでは IBinder オブジェクトが返されます。このオブジェクトは、クライアントがサービスとのやり取りに使用できるプログラミング インターフェースを定義します。

開始されたサービスにバインドする

サービスの概要で説明したように、開始とバインドの両方が可能なサービスを作成できます。つまり、startService() を呼び出してサービスを開始すると、サービスを無期限に実行できます。bindService() を呼び出して、クライアントをサービスにバインドすることもできます。

サービスを開始してバインドできるようにすると、サービスが開始されたときに、すべてのクライアントがバインドを解除しても、システムはサービスを破棄しません。サービスを停止するには、stopSelf() または stopService() を呼び出して明示的に停止する必要があります。

通常、onBind() か onStartCommand() のいずれかを実装しますが、両方の実装が必要な場合もあります。たとえば、音楽プレーヤーでは、サービスを無期限に実行し、バインディングも提供すると便利です。この方法では、アクティビティがサービスを開始して音楽を再生し、ユーザーがアプリを離れても音楽の再生が続きます。その後、ユーザーがアプリに戻ると、アクティビティがサービスにバインドして再生の制御を取り戻すことができます。

開始されたサービスにバインディングを追加する際のサービス ライフサイクルについて詳しくは、バインドされたサービスのライフサイクルを管理するをご覧ください。

クライアントがサービスにバインドするには、bindService() を呼び出します。バインド時は、ServiceConnection を実装してサービスとの接続を監視する必要があります。bindService() の戻り値は、リクエストされたサービスが存在するかどうか、クライアントがそのサービスへのアクセスを許可されているかどうかを示します。

Android システムは、クライアントとサービスの間の接続を作成するときに、ServiceConnection で onServiceConnected() を呼び出します。onServiceConnected() メソッドには IBinder 引数が含まれており、クライアントはこれを使用してバインドされたサービスと通信します。

複数のクライアントを 1 つのサービスに同時に接続できます。ただし、システムは IBinder サービス通信チャネルをキャッシュに保存します。つまり、システムは、最初のクライアントがバインドしたときにのみ、サービスの onBind() メソッドを呼び出して IBinder を生成します。その後、onBind() を再度呼び出すことなく、同じサービスにバインドする他のクライアントすべてに同じ IBinder を配信します。

最後のクライアントがサービスからバインド解除されると、システムはサービスを破棄します。ただし、サービスが startService() を使用して開始された場合は除きます。

バインドされたサービスの実装で最も重要なのは、onBind() コールバック メソッドが返すインターフェースを定義することです。次のセクションでは、サービスの IBinder インターフェースを定義するいくつかの方法について説明します。

バインドされたサービスを作成する

バインドを提供するサービスを作成する際は、IBinder を提供し、クライアントがサービスとのやり取りに使用できるプログラミング インターフェースを提供する必要があります。インターフェースを定義するには、次の 3 つの方法があります。

Binder クラスを拡張する
サービスが独自のアプリケーションに対して非公開であり、クライアントと同じプロセスで実行される場合(一般的なケース)、Binder クラスを拡張してインターフェースを作成し、onBind() からそのインスタンスを返します。クライアントは Binder を受け取り、それを使用して Binder の実装や Service で利用できるパブリック メソッドに直接アクセスできます。

サービスが単にアプリのバックグラウンド ワーカーである場合は、この方法が適しています。この方法がインターフェースの作成に適さない唯一のユースケースは、サービスが他のアプリケーションで使用される場合や、複数のプロセスで使用される場合です。

メッセンジャーを使用する
異なるプロセス間で動作するインターフェースが必要な場合は、Messenger を使用してサービスのインターフェースを作成できます。このようにして、サービスはさまざまなタイプの Message オブジェクトに応答する Handler を定義します。

この Handler は Messenger の基礎となり、IBinder をクライアントと共有して、クライアントが Message オブジェクトを使用してサービスにコマンドを送信できるようにします。さらに、クライアントは独自の Messenger を定義できるため、サービスはメッセージを返すことができます。

これは、最も簡単にプロセス間通信(IPC)を行う方法であり、Messenger がすべてのリクエストを 1 つのスレッドにキューイングするため、サービスをスレッドセーフに設計する必要がありません。

AIDL を使用する
Android インターフェース定義言語(AIDL)は、オブジェクトをオペレーティング システムが理解できるプリミティブに分解し、プロセス間でマーシャリングして IPC を実行します。Messenger を使用する前述の手法は、実際には AIDL を基盤構造としています。

前のセクションで説明したように、Messenger はすべてのクライアント リクエストのキューを単一のスレッドで作成するため、サービスはリクエストを 1 つずつ受信します。ただし、サービスで複数のリクエストを同時に処理する場合は、AIDL を直接使用できます。この場合、サービスはスレッドセーフで、マルチスレッドに対応している必要があります。

AIDL を直接使用するには、プログラミング インターフェースを定義する .aidl ファイルを作成します。Android SDK ツールはこのファイルを使用して、インターフェースを実装して IPC を処理する抽象クラスを生成します。その後、それをサービス内で拡張できます。

注: ほとんどのアプリでは、バインドされたサービスを作成するうえで AIDL は最適な選択肢ではありません。マルチスレッド機能が必要になる場合があり、実装が複雑になる可能性があるためです。そのため、このドキュメントでは、サービスでの使用方法については説明しません。AIDL を直接使用する必要があることが確実な場合は、AIDL のドキュメントをご覧ください。

Binder クラスを拡張する

ローカル アプリケーションのみがサービスを使用し、プロセス間で動作する必要がない場合は、独自の Binder クラスを実装して、クライアントがサービス内のパブリック メソッドに直接アクセスできるようにします。

注: この方法は、クライアントとサービスが同じアプリとプロセスにある場合(最も一般的なケース)にのみ有効です。たとえば、バックグラウンドで音楽を再生している独自のサービスにアクティビティをバインドする必要がある音楽アプリに適しています。

設定手順は次のとおりです。

  1. サービスで、次のいずれかを行う Binder のインスタンスを作成します。
    • クライアントが呼び出せるパブリック メソッドを格納する。
    • クライアントが呼び出せるパブリック メソッドがある、現在の Service インスタンスを返す。
    • クライアントが呼び出せるパブリック メソッドを含む、サービスでホストされた他のクラスのインスタンスを返す。
  2. Binder のインスタンスを onBind() コールバック メソッドから返します。
  3. クライアントで Binder を onServiceConnected() コールバック メソッドから受け取り、提供されたメソッドを使用して、バインドされたサービスを呼び出します。

注: クライアントが返されたオブジェクトをキャストし、その API を適切に呼び出すことができるように、サービスとクライアントは同じアプリ内にある必要があります。この手法ではプロセス間のマーシャリングは行われないため、サービスとクライアントは同じプロセスに存在する必要があります。

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 が LocalService の現在のインスタンスを取得するための getService() メソッドをクライアントに提供します。これにより、クライアントはサービス内のパブリック メソッドを呼び出すことができます。たとえば、クライアントはサービスから getRandomNumber() を呼び出せます。

ボタンがクリックされたときに LocalService にバインドして、getRandomNumber() を呼び出すアクティビティの例を次に示します。

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;
        }
    };
}

上記のサンプルは、ServiceConnection の実装と onServiceConnected() コールバックを使用して、クライアントがサービスにバインドする方法を示しています。次のセクションでは、サービスへのバインドのプロセスについて詳しく説明します。

注: 上の例では、onStop() メソッドがクライアントとサービスのバインドを解除し、クライアントのサービスへの参照を解放しています。補足事項セクションで説明されているように、適切なタイミングでクライアントをサービスからバインド解除します。

他のサンプルコードについては、ApiDemos の LocalService.java クラスと、LocalServiceActivities.java クラスをご覧ください。

メッセンジャーを使用する

リモート プロセスと通信するサービスが必要な場合は、Messenger を使用してサービスのインターフェースを提供できます。この手法を使用すると、AIDL を使用せずにプロセス間通信(IPC)を実行できます。

Messenger はサービスに対するすべての呼び出しをキューイングするため、インターフェースに Messenger を使用する方が、AIDL を使用するよりも簡単です。純粋な AIDL インターフェースは、サービスに同時リクエストを送信します。サービスは、マルチスレッドを処理する必要があります。

ほとんどのアプリケーションでは、サービスでマルチスレッドを実行する必要がないため、Messenger を使用すると、サービスは一度に 1 つの呼び出しを処理できます。サービスをマルチスレッド化することが重要な場合は、AIDL を使用してインターフェースを定義します。

Messenger の使用方法の概要は次のとおりです。

  1. サービスが、クライアントからの呼び出しごとにコールバックを受け取る Handler を実装します。
  2. サービスが、Handler を使用して Messenger オブジェクト(Handler への参照)を作成します。
  3. Messenger は、サービスが onBind() からクライアントに返す IBinder を作成します。
  4. クライアントが IBinder を使用して、サービスの Handler を参照する Messenger をインスタンス化します。クライアントはこれを使用して Message オブジェクトをサービスに送信します。
  5. サービスが、その Handler(具体的には handleMessage() メソッド)でそれぞれの Message を受け取ります。

この方法には、クライアントがサービスで呼び出すメソッドはありません。代わりに、クライアントは、サービスがその Handler で受け取るメッセージ(Message オブジェクト)を配信します。

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();
    }
}

Handler の handleMessage() メソッドは、サービスが受信 Message を受け取り、what メンバーに基づいて処理を決定する場所です。

クライアントで必要な操作は、サービスから返された IBinder に基づいて Messenger を作成し、send() を使用してメッセージを送信することだけです。たとえば、以下のアクティビティはサービスにバインドし、MSG_SAY_HELLO メッセージをサービスに配信します。

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;
        }
    }
}

この例では、サービスがクライアントに応答する方法は示されていません。サービスに応答させるには、クライアントで Messenger も作成する必要があります。クライアントは onServiceConnected() コールバックを受け取ると、send() メソッドの replyTo パラメータにクライアントの Messenger を含めた Message をサービスに送信します。

双方向メッセージを提供する方法の例については、MessengerService.java(サービス)と MessengerServiceActivities.java(クライアント)のサンプルをご覧ください。

サービスにバインドする

アプリ コンポーネント(クライアント)がサービスにバインドする際は、bindService() を呼び出します。Android システムがサービスの onBind() メソッドを呼び出し、そこからサービスとのやり取りに必要な IBinder が返されます。

バインドは非同期的に行われます。bindService() は IBinder をクライアントに返すことなく、すぐに戻ります。IBinder を受け取るには、クライアントが ServiceConnection のインスタンスを作成し、それを bindService() に渡す必要があります。ServiceConnection には、システムが IBinder を配信するために呼び出すコールバック メソッドが含まれています。

注: サービスにバインドできるのは、アクティビティ、サービス、コンテンツ プロバイダのみです。ブロードキャスト レシーバからサービスにバインドすることはできません。

クライアントからサービスにバインドする手順は、次のとおりです。

  1. ServiceConnection を実装します。

    実装では次の 2 つのコールバック メソッドをオーバーライドする必要があります。

    onServiceConnected()
    システムがこのメソッドを呼び出して、サービスの onBind() メソッドから返された IBinder を配信します。
    onServiceDisconnected()
    サービスへの接続が予期せず失われた場合(サービスがクラッシュした場合や強制終了された場合など)に、Android システムによって呼び出されます。このメソッドは、クライアントのアンバインドの際には呼び出されません。
  2. bindService() を呼び出して、ServiceConnection の実装を渡します。

    注: このメソッドから false が返された場合、クライアントにはサービスへの有効な接続がありません。ただし、クライアントで unbindService() を呼び出します。そうしないと、クライアントはアイドル状態のときにサービスがシャットダウンされないようにします。

  3. システムが onServiceConnected() コールバック メソッドを呼び出すと、インターフェースで定義されたメソッドを使用してサービスへの呼び出しを開始できます。
  4. サービスとの接続を切断するには、unbindService() を呼び出します。

    アプリによってクライアントが破棄されたときに、クライアントが引き続きサービスにバインドされている場合は、この破棄によりクライアントがアンバインドされます。サービスとのやり取りが完了したら、すぐにクライアントのバインドを解除することをおすすめします。これにより、アイドル状態のサービスがシャットダウンされます。バインドとバインド解除の適切なタイミングについて詳しくは、その他の注意事項をご覧ください。

次の例では、Binder クラスを拡張して、クライアントを以前に作成したサービスに接続します。そのため、返された IBinder を LocalBinder クラスにキャストして、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;
    }
};

この ServiceConnection を使用して、次の例のように、クライアントはそれを bindService() に渡すことでサービスにバインドできます。

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);
  • bindService() の最初のパラメータは、バインドするサービス名を明示的に指定する Intent です。

    注意: インテントを使用して Service にバインドする場合は、明示的インテントを使用して、アプリを安全に保つ必要があります。暗黙的インテントを使用してサービスを開始すると、デベロッパーはどのサービスがインテントに応答するのかを把握できず、ユーザーもどのサービスが開始するのかがわからないため、セキュリティ上のリスクを伴います。Android 5.0(API レベル 21)以降では、暗黙的インテントを使用して bindService() を呼び出すと、システムから例外がスローされます。

  • 2 つ目のパラメータは、ServiceConnection オブジェクトです。
  • 3 番目のパラメータは、バインディングのオプションを示すフラグです。通常は BIND_AUTO_CREATE を指定して、サービスがまだ有効でない場合は作成します。他に、BIND_DEBUG_UNBIND、BIND_NOT_FOREGROUND、0(なし)の値になることがあります。

その他の注意事項

サービスへのバインドに関する重要な注意点は次のとおりです。

  • 接続が切断されたときにスローされる DeadObjectException 例外を常にトラップします。リモート メソッドからスローされる例外はこれのみです。
  • オブジェクトはプロセス間で有効な参照です。
  • 通常、バインドとバインド解除は、クライアントのライフサイクルの対応する起動時と破棄時にペアとして行います。次に例を示します。
    • アクティビティが表示されている間のみサービスとやり取りする必要がある場合は、onStart() の間にバインドし、onStop() の間にバインドを解除します。
    • アクティビティがバックグラウンドで停止している間もレスポンスを受信する場合は、onCreate() でバインドし、onDestroy() でバインドを解除します。これは、アクティビティが実行されている間、バックグラウンドであっても、サービスを使用する必要があることを意味します。そのため、サービスが別のプロセスにある場合、プロセスの重要度が高くなり、システムによって強制終了される可能性が高くなります。

    注: 通常、アクティビティの onResume() コールバックと onPause() コールバックの間は、バインドとバインド解除を行いません。これらのコールバックは、ライフサイクルの移行ごとに発生するためです。これらの遷移時に発生する処理を最小限に抑えます。

    また、アプリ内の複数のアクティビティが同じサービスにバインドし、それらのアクティビティ間で切り替えが行われる場合、次のアクティビティがバインドする(再開時)前に、現在のアクティビティがバインドを解除する(一時停止時)ため、サービスが破棄されて再作成される可能性があります。アクティビティがライフサイクルを調整する方法に関するこのアクティビティの遷移については、アクティビティのライフサイクルをご覧ください。

サービスにバインドする方法を示すサンプルコードについては、ApiDemos の RemoteService.java クラスをご覧ください。

バインドされたサービスのライフサイクルを管理する

サービスがすべてのクライアントからバインド解除されると、Android システムはそのサービスを破棄します(startService() を使用して開始された場合を除く)。そのため、純粋にバインドされたサービスであれば、サービスのライフサイクルを管理する必要はありません。Android システムは、クライアントにバインドされているかどうかに基づいてサービスを管理します。

ただし、onStartCommand() コールバック メソッドを実装する場合は、サービスを明示的に停止する必要があります。これは、サービスが開始されたと見なされるためです。この場合、クライアントにバインドされているかどうかにかかわらず、サービスが stopSelf() で自ら停止するか、他のコンポーネントが stopService() を呼び出すまで、サービスは動作し続けます。

また、サービスが開始されてバインディングを受け入れる場合、システムが onUnbind() メソッドを呼び出すときに、次にクライアントがサービスにバインドしたときに onRebind() への呼び出しを受け取る場合は、必要に応じて true を返すことができます。onRebind() からは void が返されますが、クライアントは onServiceConnected() コールバックで IBinder を受け取ります。次の図は、この種類のライフサイクルのロジックを示しています。

開始され、バインドされたサービスのライフサイクル: onStartCommand() と onBind() が呼び出されます。すべてのクライアントがバインドを解除すると、onUnbind() が呼び出されます。次のバインド時に、onUnbind() が true を返した場合は onRebind() が呼び出され、それ以外の場合は onBind() が呼び出されます。サービスが停止すると、onDestroy() が呼び出されます。
図 1. 開始され、バインドも許可するサービスのライフサイクル。

開始されたサービスのライフサイクルの詳細については、サービスの概要をご覧ください。