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
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
<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
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
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
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
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
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
//取得した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
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