AndroidのBound Serviceのまとめメモ
http://d.hatena.ne.jp/ria10/20121110/1352529426の続き。
http://developer.android.com/guide/components/bound-services.html
ここを読んでのメモ。
基本
- http://d.hatena.ne.jp/ria10/20121110/1352529426に記載されているとおりBound Serviceはクライアント-サーバ形式のインターフェースを持つServiceで、Activityなどのコンポーネントと連携することができる。
- Bound Serviceの提供にはonBind()をオーバライドしIBinderを継承したクラスを返しておく必要がある。
- クライアント(Acitivityとか)はbindService()を呼ぶことによってServiceをバインドすることができる。
- bindService()が呼ばれたときはServiceとのコネクションを監視するServiceConnectionの実装を提供する必要がある。
- bindService()は即座に返り値なしでreturnされるが、システムはクライアントとServiceの接続を確立するとServiceConnectionのonServiceConnected() を呼び出し、IBinderを渡す。このIBinderインターフェースを通してクライアントとServiceは連携する。
- 複数のクライアントがServiceに接続することができるが、onBind()が呼ばれるのは最初の一度だけで、その後はonBind()は呼ばれずに最初に作られたIBinderと同じものが返される。
- 最後のクライアントがアンバインドされるとServiceは終了する。該当ServiceがstartService()によって起動してたら終了しない。(この意味がわからなかったらバックグラウンド処理に使うServiceのまとめメモを読んだほうがいい。)
- Bound Serviceを実装するにあたって最も重要なことはonBind()で返されるIBinderの実装である。
- IBinderの実装にあたってはいくつか方法があるのでここで紹介する。
Bound Serviceを作る
- IBinderの実装方法は3つある。
- Binderクラスの拡張
- Serviceが自身のアプリケーションからのみ利用されることを想定したprivateなもので、クライアントと同じプロセス内で動く(普通はこの動き)のであれば、Binderクラスを拡張したものをonBind()で返すのがよい。
- Serviceが自身のアプリケーションにとって、単なるバックグラウンド処理を担うだけのものであれば、この方法がオススメ。
- この方法が適さない場合とは、Serviceが別のアプリケーションから利用される場合(もしくは特別な実装をした別プロセスからの利用)のみ。
- Messengerを使う
- 別アプリケーションとの連携がしたいならMessengerを使う。※今回僕がこの方法は必要ないので詳細は省いちゃう。
- AIDLを使う
- AIDL (Android Interface Definition Language)を使うやりかたはMessengerを使うやりかたのベースとなる方法。
- この方法は相当複雑なのであまり使わないほうがいい。※ということでここも詳細は略。
- Binderクラスの拡張
Binderクラスの拡張
- もっとも一般的である同一アプリケーション同一プロセスで動くServiceを作る場合はBinderクラスを拡張する。
- 以下セットアップ方法
- 以下は、"クライアントから呼び出せるpublicメソッドを含んだServiceインスタンス(つまり自分自身)を返す"サンプル。
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); } }
- この例ではBinder(LocalBinder)はgetService()をクライアントに提供している。
- クライアントはこのgetService()を通してServiceを利用できる。以下はその利用サンプル。
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; } }; }
Messengerを使う
現状僕が必要ないので省略。必要になったときにここを埋めます。。。
Serviceにバインドする
- クライアントはbindService()を使うことによってServiceをバインドする。
- バインディングは非同期に行われるため、bindService()は即座に返り値なしにreturnする。
- IBinderを受け取るにはクライアントはServiceConnectionのインスタンスを作ってbindService()に渡す。
- ServiceConnectionはIBinderを渡すためのコールバックメソッドを実装しておく。
- Activity,Service,Content ProviderのみがServiceにバインドすることができる。ブロードキャストレシーバからはバインドできない。
- ということで、クライアントからServiceにバインドするには・・・
- ServiceConnectionを実装する
- bindService()を呼び、ServiceConnectionの実装を渡す。
- onServiceConnected()が呼ばれた時から、提供されたIBinderを介してServiceとの連携ができるようになる。
- コネクションを切るときはunbindService()を呼ぶ。クライアントがdestroyされればServiceからもアンバインドされるが、Serviceが不要になったタイミングでアンバインドすること。
- 以下は前述の「Binderクラスの拡張」の項で作成したServiceとコネクションを張るサンプル。
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()を呼び出せばServiceに繋げることができる。
Intent intent = new Intent(this, LocalService.class); bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
- bindService()の最初の引数はバインドするServiceを指定したIntent。
- 二番目はServiceConnectionオブジェクト。
- 最後の三番目はバインドオプション。通常はBIND_AUTO_CREATE。その他にBIND_DEBUG_UNBIND、BIND_NOT_FOREGROUND、もしくはどれでもない"0"がある。
補足
- 以下、重要な補足事項。
- コネクションが切れた時に投げられるDeadObjectExceptionを常に処理すること。
- オブジェクトはプロセス間に渡って参照がカウントされる。
- バインドとアンバインドは極力セットで利用すること。
※詳細は省くので原文を参照。