Service - Bindなし

今回はServiceを実装してみたいと思います。Context#startService()を使用したSimple版になります。下記の説明で行くと1の例。

Serviceとは

Serviceは

  1. User Interactionとは無関係に長時間処理を行うことができる
  2. ある機能を他の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が表示されるのが確認できると思います。

実装の要点を下記にまとめます

  1. manifest xmlにservice tagを追加する
  2. Serviceを拡張したclassを作成する
  3. 上記、拡張classのCallback method、onCreate()、onStartCommand()、onDestroy()にそれぞれ処理を記述する
  4. ActivityでService開始用のIntentを作成
  5. ActivityでstartService(Intent)でServiceを開始
  6. ActivityでService終了用のIntentを作成
  7. 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を試してみたいと思います。

HashTag #android, #java