page.title=バインドされたサービス parent.title=サービス parent.link=services.html @jd:body
バインドされたサービスは、クライアントサーバー インターフェースにおけるサーバーになります。バインドされたサービスでは、コンポーネント(アクティビティなど)をサービスにバインドしたり、送信を要求したり、受信を応答したり、さらにはプロセス間通信(IPC)を実行したりできるようにします。 通常、バインドされたサービスは他のアプリケーション コンポーネントを提供している間だけ機能し、バックグラウンドで無期限に実行されることはありません。
このドキュメントでは、他のアプリケーション コンポーネントからのサービスにバインドする方法を含む、バインドされたサービスの作成方法について説明します。 ただし、サービスから通知を配信する方法や、フォアグラウンドでサービスを実行するよう設定する方法など、サービス全般の詳細については、「サービス」のドキュメントをご覧ください。
バインドされたサービスは、他のアプリケーションのバインドとやり取りを可能にする {@link android.app.Service} クラスの実装です。 サービスのバインドを提供するには、{@link android.app.Service#onBind onBind()} コールバック メソッドを実装する必要があります。 このメソッドでは {@link android.os.IBinder} オブジェクトが返されます。このオブジェクトはクライアントがサービスとのやり取りに使用するプログラミング インターフェースを定義します。
サービス ドキュメントで説明されているように、開始されるサービスとバインドされるサービスの両方を作成できます。 つまり、{@link android.content.Context#startService startService()} を呼び出して開始されるサービスは無期限に実行でき、クライアントが {@link android.content.Context#bindService bindService()} を呼び出してサービスにバインドできます。
サービスの開始とバインドを許可すると、サービスが開始するとすべてのクライアントがアンバインドしても、システムによってサービスが破棄されることはありません。 代わりに、{@link android.app.Service#stopSelf stopSelf()} や {@link android.content.Context#stopService stopService()} を呼び出して、サービスを明示的に停止する必要があります。
通常実装するのは、{@link android.app.Service#onBind onBind()} か {@link android.app.Service#onStartCommand onStartCommand()} のどちらかですが、両方の実装が必要な場合もあります。 たとえば、音楽プレーヤーの場合はサービスを無期限に実行しつつ、バインドも提供できると便利な場合があります。 この方法であれば、アクティビティがサービスを開始して音楽を再生し、ユーザーがアプリケーションから離れた場合も音楽を再生し続けることができます。 次にユーザーがアプリケーションに戻ったとき、アクティビティがサービスにバインドして再生のコントロールをもう一度行えるようになります。
開始されたサービスにバインドを追加する際のサービスのライフサイクルの詳細については、バインドされたサービスのライフサイクルを管理するを必ずご覧ください。
クライアントは、{@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 つの方法があります。
サービスが単にアプリケーションのバックグラウンド ワーカーである場合は、この方法が適しています。 この方法が適していない唯一のケースは、サービスが他のアプリケーションや、別のプロセス間で使用されている場合です。
これは、最も簡単にプロセス間通信(IPC)を実行する方法であり、{@link android.os.Messenger} がすべてのリクエストを 1 つのスレッドにキューイングするため、サービスをスレッドセーフにデザインする必要がありません。
AIDL を直接使用するには、プログラミング インターフェースを定義する {@code .aidl} ファイルを作成する必要があります。 Android SDK ツールはこのファイルを使用して、インターフェースを実装して IPC を処理する抽象クラスを生成し、それをサービス内に拡張できます。
注: ほとんどのアプリケーションにおいて、マルチスレッド化が必要な点や、結果的により複雑な実装となってしまうことから、AIDL を使ったバインドされたサービスの作成はお勧めしません。 AIDL はほとんどのアプリケーションに適していないため、このドキュメントではサービスでの AIDL の使用方法については取り上げません。 どうしても AIDL の直接使用が必要な場合は、「AIDL」のドキュメントをご覧ください。
サービスがローカルのアプリケーションでのみ使用されていて、プロセス間での作業が必要ない場合は、クライアントがサービスの public メソッドに直接アクセスできるようにする独自の {@link android.os.Binder} クラスを実装できます。
注: この方法は、クライアントとサービスが同じアプリケーションとプロセスにある場合(最も一般的なケース)のみ使用できます。 たとえば、バックグラウンドで音楽を再生する独自のサービスに、アクティビティをバインドする必要のある音楽アプリケーションに適しています。
セットアップ方法は次のとおりです。
注: サービスとクライアントが同じアプリケーションになければならない理由は、クライアントが返されたオブジェクトをキャストでき、その 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} クラスをご覧ください。
IPC を実行する必要がある場合、インターフェースで {@link android.os.Messenger} を使う方が AIDL で実装するよりも簡単です。{@link android.os.Messenger} がすべての呼び出しをサービスにキューイングするのに対して、純粋な AIDL インターフェースでは同時に複数の要求をサービスに送るため、マルチスレッドの処理が必要になるためです。
ほとんどのアプリケーションにおいて、サービスはマルチスレッドを実行する必要がないため、{@link android.os.Messenger} を使用することでサービスが一度に 1 つの呼び出しを処理できます。サービスのマルチスレッド化が重視されている場合は、AIDL を使用してインターフェースを定義してください。
リモート プロセスと通信するサービスが必要な場合は、{@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} の配信用に呼び出すコールバック メソッドが含まれています。
注: サービスにバインドできるのは、アクティビティ、サービス、コンテンツ プロバイダのみです。ブロードキャスト レシーバーからサードパーティビスにはバインドできません。 —
そのため、クライアントからサービスにバインドするには次の操作が必要です。
実装では次の 2 つのコールバック メソッドをオーバーライドする必要があります。
クライアントが破棄されたときにはサービスからアンバウンドしますが、サービスが使用されていないときはシャットダウンできるよう、サービスとのやり取りが終了したときや、アクティビティが停止したときは常にアンバウンドする必要があります (バインドとアンバインドの適切なタイミングについては後半でさらに詳しく説明します)。
たとえば、次のスニペットは 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);
サービスへのバインドに関する重要な注意点は次のとおりです。
注: 通常、アクティビティの {@link android.app.Activity#onResume onResume()} と {@link android.app.Activity#onPause onPause()} の間にはバインドとアンバインドは行いません。これは、これらのコールバックがライフサイクルの遷移すべてで発生するため、その遷移で発生するプロセスを最小限に抑える必要があるためです。 また、アプリケーションの複数のアクティビティが同一サービスにバインドしていて、そのなかの 2 つのアクティビティ間の遷移が生じる場合、現在のアクティビティは次のアクティビティがバインドする(再開中)前にアンバインドされる(停止中)ため、サービスが破棄されて再作成される場合があります (ライフサイクルと連携したアクティビティの遷移の詳細については、「Activities」のドキュメントをご覧ください)。
他のサンプル コードについては、 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. 開始されたサービスでバインドを許可するサービスのライフサイクル
開始されたサービスのライフサイクルの詳細については、「サービス」のドキュメントをご覧ください。