Serviceを使う(1) LocalServiceによる常駐型アプリ
|Androidで常駐型アプリケーションを作成する場合に便利なServiceについてライフサイクル・使い方を解説します。サービスの利用例はステータス通知(Notification)を変化させる等をご確認ください。Serviceを使う(1)では簡単化のため、Remote Messenger Serviceを次回以降として、LocalServiceに特化して解説します。
- Serviceのライフサイクル
- onCreate / onStartCommand / onDestroyの3つの状態遷移
- サービスの実行方法によってライフサイクルが異なる
- サービスの実行方法はContext#startServiceとContext#bindServiceの2種類
- startService/stopService
- Service全般として実行中はServiceからActivityへIntentの発行が可能
- サービス起動後はActivityからServiceを制御する経路がない
- Serviceの生存期間はActivityに依存しない。明示的にstopServiceが呼ばれるまで動き続ける。
- bindService/unbindService
- バインド(bind)という仕組みを使い、ActvitiyとServiceでコネクションを確立する(接続する)
- バインドを使うことでActivityからServiceを制御できる
- Serviceの生存期間はコネクションに依存。コネクションが切断されるとServiceは終了する。
サンプルコード、詳細は以下から。はじめに一般的なサービスのライフサイクル、startServiceによるサービス起動を紹介します。バインドのサンプルコードは記事後半になるので、適宜読み飛ばしてください。
サービスのライフサイクル
Context#startServiceによる実行の場合、サービスのライフサイクルはonCreate / onStartCommand / onDestroy の3つのコールバックメソッドが呼ばれます。
- public void onCreate()
- public void onDestroy()
- public int onStartCommand(Intent intent, int flags, int startId)
onCreateはサービスのインスタンス生成時(複数回startServiceを実行した場合、初回のみ)呼び出されます。onStartCommandメソッドではstartServiceで送られたIntentを受けとります。
stopServiceメソッドによるサービス終了のタイミングでonDestroyが呼ばれます。ActivityでstopServiceを呼ばず、アプリケーションを終了した場合はServiceは終了せず、バックグラウンドで動き続けます。
※IntentはIntentを使って画面を遷移する(明示的Intent)を参照してください。
サンプルコード
サンプルとして以下の画像のようにServiceを実行するボタンを持つActivityを準備します
サービスの実装
MyService.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | public class MyService extends Service { static final String TAG= "LocalService" ; @Override public void onCreate() { Log.i(TAG, "onCreate" ); Toast.makeText( this , "MyService#onCreate" , Toast.LENGTH_SHORT).show(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i(TAG, "onStartCommand Received start id " + startId + ": " + intent); Toast.makeText( this , "MyService#onStartCommand" , Toast.LENGTH_SHORT).show(); // 強制終了時、システムによる再起動を求める場合はSTART_STICKYを利用 // 再起動が不要な場合はSTART_NOT_STICKYを利用する return START_STICKY; } @Override public void onDestroy() { Log.i(TAG, "onDestroy" ); Toast.makeText( this , "MyService#onDestroy" , Toast.LENGTH_SHORT).show(); } } |
6,12,20行目:コールバックメソッドのみ実装した簡単なServiceです。サービスを使う際は、AndroidManifestに記述を追加する必要があります。
AndroidManifest.xml
1 2 3 4 5 6 7 8 9 10 11 | <application android:icon= "@drawable/icon" android:label= "@string/app_name" > <activity android:name= "ServiceControllerActivity" android:label= "@string/app_name" > <intent-filter> <action android:name= "android.intent.action.MAIN" /> <category android:name= "android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:name= "MyService" /> </application> |
サービスを起動するActivityの実装
ServiceControllerActivity.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | public class ServiceControllerActivity extends Activity { public void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.main); Button btn = (Button) findViewById(R.id.StartButton); btn.setOnClickListener(btnListener); //リスナの登録 btn = (Button) findViewById(R.id.StopButton); btn.setOnClickListener(btnListener); //リスナの登録 } private OnClickListener btnListener = new OnClickListener() { public void onClick(View v) { switch (v.getId()){ case R.id.StartButton: //startServiceでサービスを起動 startService( new Intent(ServiceControllerActivity. this , MyService. class )); break ; case R.id.StopButton: //stopServiceでサービスの終了 stopService( new Intent(ServiceControllerActivity. this , MyService. class )); break ; } } }; |
13行目から始まるOnClickListener(ボタンが押下されたときに呼び出される)でサービスの起動/終了を行っています。
19行目、22行目でIntent(Context, Class)の形でIntentを生成してstartService/stopServiceの引数にして、サービス(ここではMyService)を呼び出します。
StartButton、StopButtonの順番で押下した際のログでライフサイクルを確認してみます。
Logcat
1 2 3 | 02 - 15 06 : 45 : 10.280 : INFO/LocalService( 1619 ): onCreate 02 - 15 06 : 45 : 10.290 : INFO/LocalService( 1619 ): onStartCommand Received start id 1 : Intent { cmp=org.jpn.techbooster.sample.serviceActivity/.MyService } 02 - 15 06 : 45 : 18.170 : INFO/LocalService( 1619 ): onDestroy |
StartButton、StartButton、StopButtonの順番で押下すると、onCreateメソッドの呼び出しが初回のみであることがわかります。
Logcat
1 2 3 4 | 02 - 15 06 : 45 : 19.530 : INFO/LocalService( 1619 ): onCreate 02 - 15 06 : 45 : 19.540 : INFO/LocalService( 1619 ): onStartCommand Received start id 1 : Intent { cmp=org.jpn.techbooster.sample.serviceActivity/.MyService } 02 - 15 06 : 45 : 20.170 : INFO/LocalService( 1619 ): onStartCommand Received start id 2 : Intent { cmp=org.jpn.techbooster.sample.serviceActivity/.MyService } 02 - 15 06 : 45 : 22.080 : INFO/LocalService( 1619 ): onDestroy |
バインド利用時のサービスのライフサイクル
![]() |
![]() |
バインド利用時のライフサイクル | startService利用時のライフサイクル |
バインド(bind)という仕組みを使い、ActvitiyとServiceを接続するケースでは、ライフサイクルが多少異なります。図にある通り、onStartCommandメソッドが呼び出されることがありません。
通常のonCreate/onDestroyに加えて、バインド時は以下のコールバックメソッドを利用します。
- public abstract IBinder onBind (Intent intent)
- public boolean onUnbind (Intent intent)
- public void onRebind (Intent intent)
onBindはバインド(接続時)、Unbindはバインド解除(切断時)に呼び出されます。onRebindメソッドは図中には現れませんが、Unbind後に再接続する場合、onRebindメソッドを利用することができます。使う際はonUnbind メソッドの返り値をtrueに設定します。
サンプルコード
図のActivitiy下2つのボタン部分とServiceにバインドを実装します。
バインドの実装
MyService.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | public class MyService extends Service { // onCreate , onStartCommand , onDestroy() はstartServiceと同じなので省略 /* * 以下はBind時に必要なコード * */ //サービスに接続するためのBinder public class MyServiceLocalBinder extends Binder { //サービスの取得 MyService getService() { return MyService. this ; } } //Binderの生成 private final IBinder mBinder = new MyServiceLocalBinder(); @Override public IBinder onBind(Intent intent) { Toast.makeText( this , "MyService#onBind" + ": " + intent, Toast.LENGTH_SHORT).show(); Log.i(TAG, "onBind" + ": " + intent); return mBinder; } @Override public void onRebind(Intent intent){ Toast.makeText( this , "MyService#onRebind" + ": " + intent, Toast.LENGTH_SHORT).show(); Log.i(TAG, "onRebind" + ": " + intent); } @Override public boolean onUnbind(Intent intent){ Toast.makeText( this , "MyService#onUnbind" + ": " + intent, Toast.LENGTH_SHORT).show(); Log.i(TAG, "onUnbind" + ": " + intent); //onUnbindをreturn trueでoverrideすると次回バインド時にonRebildが呼ばれる return true ; } } |
18行目、25行目、31行目それぞれonBind、onRebind、onUnbindメソッドを実装、Toastを表示します。
サービスを起動するActivityの変更
ボタンのOnClickListenerにbindService/unbindServiceを追加します。
ServiceControllerActivity.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | private OnClickListener btnListener = new OnClickListener() { public void onClick(View v) { switch (v.getId()){ case R.id.StartButton: //startServiceでサービス起動 startService( new Intent(ServiceControllerActivity. this , MyService. class )); break ; case R.id.StopButton: //stopServiceでサービス終了 stopService( new Intent(ServiceControllerActivity. this , MyService. class )); break ; case R.id.BindButton: //doBindService doBindService(); break ; case R.id.UnbindButton: //doUnbindService doUnbindService(); break ; default : break ; } } }; |
バインドするためのServiceConnectionを実装
バインドするためにはServiceConnectionを使います。Activity側でServiceConnectionと以下のServiceConnectionのメソッドを用意します。
- public void onServiceConnected(ComponentName className, IBinder service)
- public void onServiceDisconnected(ComponentName className)
ServiceConnection#onServiceConnectedメソッドはActivityとServiceのコネクションが確立した際に呼び出されます。
引数のIBinder serviceで、サービスのバインダーを受け取れます。バインダー経由でServiceのインスタンスを取得することが可能、つまりダイレクトにサービス側のメソッド呼び出しができるようになります。
ServiceConnection#onServiceDisconnectedメソッドはプロセスのクラッシュなど意図しないサービスの切断が発生した場合に利用されます(呼び出されること自体、あまり好ましい状況ではないでしょう)。
ServiceControllerActivity.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | //取得したServiceの保存 private MyService mBoundService; private boolean mIsBound; private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { // サービスとの接続確立時に呼び出される Toast.makeText(ServiceControllerActivity. this , "Activity:onServiceConnected" , Toast.LENGTH_SHORT).show(); // サービスにはIBinder経由で#getService()してダイレクトにアクセス可能 mBoundService = ((MyService.MyServiceLocalBinder)service).getService(); //必要であればmBoundServiceを使ってバインドしたサービスへの制御を行う } public void onServiceDisconnected(ComponentName className) { // サービスとの切断(異常系処理) // プロセスのクラッシュなど意図しないサービスの切断が発生した場合に呼ばれる。 mBoundService = null ; Toast.makeText(ServiceControllerActivity. this , "Activity:onServiceDisconnected" , Toast.LENGTH_SHORT).show(); } }; void doBindService() { //サービスとの接続を確立する。明示的にServiceを指定 //(特定のサービスを指定する必要がある。他のアプリケーションから知ることができない = ローカルサービス) bindService( new Intent(ServiceControllerActivity. this , MyService. class ), mConnection, Context.BIND_AUTO_CREATE); mIsBound = true ; } void doUnbindService() { if (mIsBound) { // コネクションの解除 unbindService(mConnection); mIsBound = false ; } } |
サンプル内、doBindServiceメソッドはサービスにバインド、doUnbindServiceメソッドはサービスをアンバインド(切断)するメソッドです。
BindButton、UnbindButtonの順番で押下した際のログでライフサイクルを確認してみます。
Logcat
1 2 3 4 | 02 - 15 07 : 25 : 51.090 : INFO/LocalService( 1619 ): onCreate 02 - 15 07 : 25 : 51.110 : INFO/LocalService( 1619 ): onBind: Intent { cmp=org.jpn.techbooster.sample.serviceActivity/.MyService } 02 - 15 07 : 25 : 52.890 : INFO/LocalService( 1619 ): onUnbind: Intent { cmp=org.jpn.techbooster.sample.serviceActivity/.MyService } 02 - 15 07 : 25 : 52.890 : INFO/LocalService( 1619 ): onDestroy |