Service - Bindなし
今回はServiceを実装してみたいと思います。Context#startService()を使用したSimple版になります。下記の説明で行くと1の例。
Serviceとは
Serviceは
- User Interactionとは無関係に長時間処理を行うことができる
- ある機能を他のapplicationに提供できる
というものです。
1に関する補足説明としては、ActivityがUser Eventによりある処理の途中で終了されてしまったりするのに対して、Serviceは独立して処理を続けることができます。Context#startService()で呼び出されるServiceがこれに該当します。
2の例でいくと、Backgroundで動いているmusic playerなんかがその例になります。あとは複数のapplicationで機能を共通化したい場合にも有効です。Context#bindService()で呼び出されるServiceがこれに該当します。(Android Developers siteはここ)
今回は単純にServiceをstartするButtonを押すとServiceのstart処理を開始し、ServiceのCallback method内でToastを表示し、
stopするButtonを押すとServiceを停止処理を開始し、またCallback methodでToastを表示するというものを作成しました。
ただ、onStartCommand() method内ではThreadを作成し、その中でUI threadのHandlerにToastを表示する処理をpostし、
Threadの処理が終了する時点でServiceが終了していなければService#stopSelf() methodを呼び、Serviceを停止しています。
Home buttonでActivityをstopしてもServiceからのToastが表示されるのが確認できると思います。
実装の要点を下記にまとめます
- manifest xmlにservice tagを追加する
- Serviceを拡張したclassを作成する
- 上記、拡張classのCallback method、onCreate()、onStartCommand()、onDestroy()にそれぞれ処理を記述する
- ActivityでService開始用のIntentを作成
- ActivityでstartService(Intent)でServiceを開始
- ActivityでService終了用のIntentを作成
- ActivityでstopService(Intent)でServiceを終了(もしくはServiceでstopSelf()し停止)
気づいた点をまとめます
- 一旦、Serviceが起動後はstartService()をするとonStartCommand()が呼ばれる(onCreate()は呼ばれない)
注意点を下記にまとめます
- Serviceは特に指示しない限り、そのServiceが属するapplicationのProcessで動きます
- Serviceはmain threadでcallback methodが呼ばれます。処理をService化したからといって重たい処理の場合ANRを避けることはできません
んで、下記が単純な実装です
Manifest file抜粋
<application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".MyActivity" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <!-- manifest xmlにservice tagを追加する "android:name"属性にServiceの名前を定義。 ServiceのProcessを分けたいなら"android:process"属性にProcess名を定義 --> <service android:name=".MyService" android:process=":remote"/> </application>
Serviceを拡張したclass
package com.android.practice; import android.app.Service; import android.content.Context; import android.content.Intent; import android.os.Handler; import android.os.IBinder; import android.widget.Toast; //Serviceを拡張したclassを作成する public class MyService extends Service { private Context mContext; private Handler mHandler; @Override public IBinder onBind(Intent arg0) { //今回は何もしません return null; } //拡張classのCallback method、onCreate()、onStartCommand()、onDestroy()にそれぞれ処理を記述する @Override public void onCreate() { super.onCreate(); mContext = this; mHandler = new Handler(); //Toastを表示する Toast.makeText(this, "Hi MyService is on create called by " + Thread.currentThread().getName() + " :)", Toast.LENGTH_SHORT).show(); } //拡張classのCallback method、onCreate()、onStartCommand()、onDestroy()にそれぞれ処理を記述する @Override public int onStartCommand(Intent intent, int flags, int startId) { //Toastを表示する Toast.makeText(this, "Hi MyService is on startCommand called by " + Thread.currentThread().getName() + " :)", Toast.LENGTH_SHORT).show(); //ThreadでSleep後、Toastを表示してみる new Thread("WorkerThread"){ public void run(){ try{ Thread.sleep(10000); final String thisThreadName = Thread.currentThread().getName(); mHandler.post(new Runnable(){ public void run(){ Toast.makeText(mContext, "Hi this is a toast posted to " + Thread.currentThread().getName() + " by " + thisThreadName + " :)", Toast.LENGTH_SHORT).show(); } }); //ServiceでstopSelf()し停止 //ActivityからstopService()が呼ばれていなかったら自分でstopするようにする stopSelf(); }catch(InterruptedException e){ } } }.start(); //START_STICKYを返した場合、onStartCommandから戻ってきてstart処理をしている時にProcessがkillされたても、 //再度、onStartCommandが呼ばれる //ただ、pending中のstart commandがない場合、IntentがnullになってしまうのでIntentを使用する際は注意が必要 return START_STICKY; } //拡張classのCallback method、onCreate()、onStartCommand()、onDestroy()にそれぞれ処理を記述する @Override public void onDestroy() { super.onDestroy(); //Toastを表示する Toast.makeText(this, "Hi MyService is on destroy called by " + Thread.currentThread().getName() + " :(", Toast.LENGTH_LONG).show(); } }
Activity
package com.android.practice; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; public class MyActivity extends Activity { private Context mContext; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mContext = this; setContentView(R.layout.main); //xmlからButtonを取得 Button startButton = (Button)findViewById(R.id.start_button); Button stopButton = (Button)findViewById(R.id.stop_button); //startButtonにListenerを登録 startButton.setOnClickListener(new OnClickListener(){ public void onClick(View v) { //ActivityでService開始用のIntentを作成 Intent intent = new Intent(mContext, MyService.class); //ActivityでstartService(Intent)でServiceを開始 startService(intent); } }); //stopButtonにListenerを登録 stopButton.setOnClickListener(new OnClickListener(){ public void onClick(View v) { //ActivityでService終了用のIntentを作成 Intent intent = new Intent(mContext, MyService.class); //ActivityでstopService(Intent)でServiceを終了 stopService(intent); } }); } }
というような感じです。Activityの起動方法とさして変わりがないと思います。
んで、次回はServiceとやりとりが出来る様にBindを試してみたいと思います。