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

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

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

基本情報

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

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

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

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

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

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

クライアントがサービスにバインドするには、bindService() を呼び出します。バインド時は、ServiceConnection を実装してサービスとの接続を監視する必要があります。bindService() の戻り値は、リクエストされたサービスが存在するかどうかと、クライアントがそのサービスへのアクセスを許可されているかどうかを示します。Android システムは、クライアントとサービスとの間の接続を作成すると、ServiceConnectiononServiceConnected() を呼び出します。onServiceConnected() メソッドには、IBinder 引数が含まれており、クライアントはこの引数を使用してバインドされたサービスと通信します。

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

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

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

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

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

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

サービスが単にアプリのバックグラウンド ワーカーである場合は、この方法が適しています。この方法が適していない唯一のケースは、サービスが他のアプリで使用される場合や、異なるプロセス間で使用される場合です。

メッセンジャーを使用する
異なるプロセス間で動作するインターフェースが必要な場合は、Messenger を使用してサービスのインターフェースを作成できます。この方法では、サービスが、異なるタイプの Message オブジェクトに応答する Handler を定義します。この Handler を基本として MessengerIBinder をクライアントと共有でき、クライアントは Message オブジェクトを使用してサービスにコマンドを送信できるようになります。さらに、クライアントは、サービスがメッセージを返信できるように独自の Messenger を定義できます。

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

AIDL を使用する
Android インターフェース定義言語(AIDL)は、オブジェクトをオペレーティング システムが理解できるプリミティブに分解し、それらをプロセス間でマーシャルすることで IPC を行います。前述の Messenger を使用する方法は、実際には AIDL を基本構造としています。前述したように、Messenger はすべてのクライアント リクエストのキューを 1 つのスレッドに作成するため、サービスはリクエストを一度に 1 つずつ受け取ります。ただし、サービスで複数のリクエストを同時に処理する場合は、AIDL を直接使用できます。この場合、サービスはスレッドセーフであり、かつマルチスレッドに対応している必要があります。

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

注: ほとんどのアプリにおいて、バインドされたサービスを作成する際に AIDL を使用することはおすすめしません。マルチスレッド化が必要になる可能性があり、結果としてより複雑な実装になってしまうためです。AIDL はほとんどのアプリに適していないため、このドキュメントではサービスでの AIDL の使用方法については取り上げません。どうしても AIDL を直接使用する必要がある場合は、AIDL のドキュメントをご覧ください。

Binder クラスを拡張する

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

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

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

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

注: クライアントが返されたオブジェクトをキャストし、その 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);
    }
}

LocalBinderLocalService の現在のインスタンスを取得するための getService() メソッドをクライアントに提供します。これにより、クライアントがサービスのパブリック メソッドを呼び出せるようになります。たとえば、クライアントはサービスから getRandomNumber() を呼び出せます。

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

Kotlin

class BindingActivity : Activity() {
    private lateinit var mService: LocalService
    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) {
            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)
        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 were something that might hang, then this request should
            // occur in a separate thread to avoid slowing down the activity performance.
            val num: Int = mService.randomNumber
            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);
        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 were something that might hang, then this request should
            // occur 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) {
            mBound = false;
        }
    };
}

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

注: 上記の例では、onStop() メソッドがクライアントをサービスからアンバインドしています。その他の注意事項で説明されているように、クライアントは適切なタイミングでサービスからアンバインドする必要があります。

他のサンプルコードについては、ApiDemosLocalService.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();
    }
}

HandlerhandleMessage() メソッドは、サービスが 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)
            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);
            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 クラスを拡張して上記で作成したサービスにクライアントを接続しているため、必要な処理は、返された IBinderLocalBinder クラスにキャストして、LocalService インスタンスをリクエストすることだけです。

Kotlin

var mService: LocalService

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")
        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");
        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_UNBINDBIND_NOT_FOREGROUND です。何も指定しない場合は 0 に設定します。

その他の注意事項

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

  • 接続が切れたときにスローされる DeadObjectException 例外は、常にトラップする必要があります。リモート メソッドからスローされる例外はこれのみです。
  • オブジェクトはプロセス間で有効な参照です。
  • 通常は、クライアントのライフサイクルの開始と終了のタイミングに合わせて、その間でバインドとアンバインドをペア設定します。次の例をご覧ください。
    • アクティビティが見えている間のみサービスとやり取りする必要がある場合は、onStart() の間にバインドし、onStop() の間にアンバインドします。
    • アクティビティがバックグラウンドで停止している間も応答を受け取るようにする場合は、onCreate() の間にバインドし、onDestroy() の間にアンバインドします。つまり、アクティビティの実行中(バックグラウンドも含む)は常にサービスを使用する必要があるため、サービスが別のプロセスにある場合は、プロセスの重みを上げて、システムに強制終了させやすいようにします。

    注: アクティビティの onResume()onPause() の間にはバインドとアンバインドを行いません。これらのコールバックはすべてのライフサイクル遷移で発生し、その遷移で発生するプロセスを最小限に抑える必要があるためです。また、アプリの複数のアクティビティが同一サービスにバインドしていて、その中の 2 つのアクティビティ間で遷移が生じる場合、現在のアクティビティは、次のアクティビティが(再開中に)バインドされる前に(停止中に)アンバインドされるため、サービスが破棄されて再作成されることがあります。ライフサイクルと連携したアクティビティの遷移について詳しくは、アクティビティのドキュメントをご覧ください。

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

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

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

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

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

図 1. 開始されたサービスでバインドも許可する場合のサービスのライフサイクル。

開始されたサービスのライフサイクルについて詳しくは、サービスのドキュメントをご覧ください。