page.title=バインドされたサービス parent.title=サービス parent.link=services.html @jd:body

    本書の内容

    1. 基本
    2. バインドされたサービスを作成する
      1. Binder クラスを拡張する
      2. メッセンジャーを使用する
    3. サービスにバインドする
    4. バインドされたサービスのライフサイクルを管理する

    キークラス

    1. {@link android.app.Service}
    2. {@link android.content.ServiceConnection}
    3. {@link android.os.IBinder}

    サンプル

    1. {@code RemoteService}
    2. {@code LocalService}

    関連ドキュメント

    1. サービス

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

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

基本

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

クライアントは、{@link android.content.Context#bindService bindService()} を呼び出せばサービスにバインドできます。バインドするときは、サービスとの接続を監視する {@link android.content.ServiceConnection} を実装する必要があります。{@link android.content.Context#bindService bindService()} メソッドは値なしですぐに返されますが、Android システムがクライアントとサービス間の接続を作成すると {@link android.content.ServiceConnection} の {@link android.content.ServiceConnection#onServiceConnected onServiceConnected()} が呼び出され、クライアントがサービスとの通信に使用できる {@link android.os.IBinder} が配信されます。

複数のクライアントが同時にサービスに接続できます。ただし、1 つ目のクライアントのバインドの際にのみ、システムはサービスの {@link android.app.Service#onBind onBind()} メソッドを呼び出して{@link android.os.IBinder} を取得します。 その後システムは {@link android.app.Service#onBind onBind()} を呼び出すことなく、同じ {@link android.os.IBinder} をバインドしたすべてのクライアントに配信します。

最後のクライアントがサービスからアンバインドされると、システムはサービスを破棄します(サービスが {@link android.content.Context#startService startService()} でも開始された場合を除く)。

バインドされたサービスを実装するときに最も重要なのは、{@link android.app.Service#onBind onBind()} コールバック メソッドが返すインターフェースを定義することです。 サービスの {@link android.os.IBinder} インターフェースの定義には複数の方法があり、次のセクションではそれぞれのテクニックについて説明していきます。

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

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

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

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

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

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

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

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

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

Binder クラスを拡張する

サービスがローカルのアプリケーションでのみ使用されていて、プロセス間での作業が必要ない場合は、クライアントがサービスの public メソッドに直接アクセスできるようにする独自の {@link android.os.Binder} クラスを実装できます。

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

セットアップ方法は次のとおりです。

  1. サービスで次のいずれかに該当する {@link android.os.Binder} のインスタンスを作成します。
  2. {@link android.os.Binder} のインスタンスを {@link android.app.Service#onBind onBind()} コールバック メソッドから返します。
  3. クライアントで、{@link android.os.Binder} を {@link android.content.ServiceConnection#onServiceConnected onServiceConnected()} コールバック メソッドから受け取り、提供されたメソッドを使用してバインドされたサービスを呼び出します。

注: サービスとクライアントが同じアプリケーションになければならない理由は、クライアントが返されたオブジェクトをキャストでき、その API を適切に呼び出せるようにするためです。 また、サービスとクライアントが同じプロセスになければならない理由は、この方法ではプロセス間の整理が一切行われないためです。

以下は、{@link android.os.Binder} の実装を介してサービスのメソッドにクライアントアクセスを提供するサービスの例です。

public class LocalService extends Service {
    // Binder given to clients
    private final IBinder mBinder = 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 mBinder;
    }

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

{@code LocalBinder} が {@code LocalService} の現在のインスタンスを取り出すための {@code getService()}メソッドをクライアントに提供します。 これにより、クライアントがサービス内の public メソッドを呼び出せるようになります。 たとえば、クライアントはサービスから {@code getRandomNumber()} を呼び出すことができます。

以下は、{@code LocalService} にバインドして、ボタンがクリックされたときに {@code getRandomNumber()} を呼び出すアクティビティの例です。

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, mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        // Unbind from the service
        if (mBound) {
            unbindService(mConnection);
            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 mConnection = 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;
        }
    };
}

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

注: 上記の例ではサービスから明示的にアンバウンドしていませんが、すべてのクライアントは適切なタイミング(アクティビティが一時停止したときなど)でアンバウンドする必要があります。

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

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

リモート プロセスと通信するサービスが必要な場合は、{@link android.os.Messenger} を使用してサービスのインターフェースを提供できます。 この方法では、AIDL を使用する必要なくプロセス間通信(IPC)を実行できます。

{@link android.os.Messenger} の使用方法の概要は以下のとおりです。

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

以下は、{@link android.os.Messenger} インターフェースを使用するサービスの簡単な例です。

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.
     */
    class IncomingHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_SAY_HELLO:
                    Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    /**
     * Target we publish for clients to send messages to IncomingHandler.
     */
    final Messenger mMessenger = new Messenger(new IncomingHandler());

    /**
     * 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();
        return mMessenger.getBinder();
    }
}

{@link android.os.Handler} の {@link android.os.Handler#handleMessage handleMessage()} メソッドが、サービスが {@link android.os.Message} を受け取る場所であり、{@link android.os.Message#what} メンバーに基づいてその後の操作を決める場面であることに注目してください。

クライアントで必要な操作は、サービスから返された {@link android.os.IBinder} に基づいて {@link android.os.Messenger} を作成し、{@link android.os.Messenger#send send()} を使用してメッセージを送信するだけです。例として、サービスにバインドして {@code MSG_SAY_HELLO} メッセージをサービスに送信する簡単なアクティビティを次に示します。

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

    /**
     * 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);
            mBound = 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;
            mBound = false;
        }
    };

    public void sayHello(View v) {
        if (!mBound) 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 (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }
}

この例では、サービスがクライアントにどのように応答できるのかは示されていません。サービスが応答するようにするには、クライアントで {@link android.os.Messenger} も作成する必要があります。 その後、クライアントが {@link android.content.ServiceConnection#onServiceConnected onServiceConnected()} コールバックを受け取るとき、{@link android.os.Messenger#send send()} メソッドの {@link android.os.Message#replyTo} パラメータにクライアントの{@link android.os.Messenger} を含めてサービスに {@link android.os.Message} を送信します。

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

サービスにバインドする

アプリケーションのコンポーネント(クライアント)は、{@link android.content.Context#bindService bindService()} を呼び出してサービスにバインドできます。 次に Android システムがサービスの {@link android.app.Service#onBind onBind()} メソッドを呼び出し、そこからサービスとのやり取りに必要な {@link android.os.IBinder} が返されます。

バインドは非同期的に行われます。{@link android.content.Context#bindService bindService()} は瞬時に返され、{@link android.os.IBinder} はクライアントには返されません。 {@link android.os.IBinder} を受け取るには、クライアントが {@link android.content.ServiceConnection} のインスタンスを作成し、それを {@link android.content.Context#bindService bindService()} に渡す必要があります。{@link android.content.ServiceConnection} にはシステムが {@link android.os.IBinder} の配信用に呼び出すコールバック メソッドが含まれています。

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

そのため、クライアントからサービスにバインドするには次の操作が必要です。

  1. {@link android.content.ServiceConnection} を実装する。

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

    {@link android.content.ServiceConnection#onServiceConnected onServiceConnected()}
    システムがこれを呼び出して、サービスの {@link android.app.Service#onBind onBind()} メソッドから返された {@link android.os.IBinder} を配信します。
    {@link android.content.ServiceConnection#onServiceDisconnected onServiceDisconnected()}
    サービスがクラッシュしたり強制終了されたりした場合など、サービスへの接続が予期せず失われたときに、Android システムがこれを呼び出します。 これは、クライアントのアンバウンドの際には呼び出されません
  2. {@link android.content.Context#bindService bindService()} を呼び出して、{@link android.content.ServiceConnection} の実装を渡します。
  3. システムが {@link android.content.ServiceConnection#onServiceConnected onServiceConnected()} コールバック メソッドを呼び出すと、インターフェースで定義されたメソッドを使用してサービスへの呼び出しを開始できます。
  4. サービスとの接続を切断するには、{@link android.content.Context#unbindService unbindService()} を呼び出します。

    クライアントが破棄されたときにはサービスからアンバウンドしますが、サービスが使用されていないときはシャットダウンできるよう、サービスとのやり取りが終了したときや、アクティビティが停止したときは常にアンバウンドする必要があります (バインドとアンバインドの適切なタイミングについては後半でさらに詳しく説明します)。

たとえば、次のスニペットは Binder クラスを拡張して先ほど作成したサービスにクライアントを接続しており、返された {@link android.os.IBinder} を {@code LocalService} クラスにキャストして、{@code LocalService} インスタンスを要求することだけが必要になります。

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

この {@link android.content.ServiceConnection} を使用して、クライアントは {@link android.content.Context#bindService bindService()} に渡すことでサービスにバインドできます。 次に例を示します。

Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);

その他の注意点

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

他のサンプル コードについては、 ApiDemos{@code RemoteService.java} クラスをご覧ください。

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

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

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

さらに、サービスが開始されてバインドを許可する場合、システムが {@link android.app.Service#onUnbind onUnbind()} メソッドを呼び出すとき、次回クライアントがサービスにバインドするときに {@link android.app.Service#onRebind onRebind()} への呼び出しを受け取りたい場合は、{@code true} を返すようにすることもできます({@link android.app.Service#onBind onBind()} への呼び出しを受け取る代わりに)。{@link android.app.Service#onRebind onRebind()} からは void が返されますが、クライアントは {@link android.content.ServiceConnection#onServiceConnected onServiceConnected()} コールバックで {@link android.os.IBinder} を受け取ります。下の図 1 は、ライフサイクルのこの種の論理を表しています。

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

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