Skip to content

Most visited

Recently visited

navigation

バインドされたサービス

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

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

基本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Binder クラスを拡張する

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

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

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

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

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

以下は、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);
    }
}

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

以下は、LocalService にバインドして、ボタンがクリックされたときに 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;
        }
    };
}

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

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

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

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

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

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

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

以下は、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();
    }
}

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

クライアントで必要な操作は、サービスから返された IBinder に基づいて Messenger を作成し、send() を使用してメッセージを送信することだけです。例として、サービスにバインドして 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;
        }
    }
}

この例では、サービスがクライアントにどのように応答できるのかは示されていません。サービスが応答するようにするには、クライアントで 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 の実装を渡します。
  3. システムが onServiceConnected() コールバック メソッドを呼び出すと、インターフェースで定義されたメソッドを使用してサービスへの呼び出しを開始できます。
  4. サービスとの接続を切断するには、unbindService() を呼び出します。

    アプリによってクライアントが破棄されたときに、クライアントが引き続きサービスにバインドされている場合は、この破棄によりクライアントがアンバインドされます。 クライアントがサービスとの連携処理を完了した直後に、クライアントをアンバインドすることをお勧めします。 このようにすると、アイドル状態のサービスをシャットダウンすることができます。バインドとアンバインドの適切なタイミングについては、その他の注意点を参照してください。

たとえば、次のスニペットは Binder クラスを拡張して先ほど作成したサービスにクライアントを接続しており、必要な処理は返された IBinderLocalService クラスにキャストして、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;
    }
};

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

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

その他の注意点

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

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

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

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

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

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

図 1 サービスの開始とバインド許可のライフサイクル

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

This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Take a one-minute survey?
Help us improve Android tools and documentation.