Android: IntentServiceを利用したサービスのサンプル

AndroidのIntentServiceクラスを利用したサービスのサンプルです。

[IntentServiceについて]
http://developer.android.com/guide/topics/fundamentals/services.html

Extending the IntentService class
Because most started services don't need to handle multiple requests simultaneously (which can actually be a dangerous multi-threading scenario), it's probably best if you implement your service using the IntentService class.

The IntentService does the following:
* Creates a default worker thread that executes all intents delivered to onStartCommand() separate from your application's main thread.
* Creates a work queue that passes one intent at a time to your onHandleIntent() implementation, so you never have to worry about multi-threading.
* Stops the service after all start requests have been handled, so you never have to call stopSelf().
* Provides default implementation of onBind() that returns null.
* Provides a default implementation of onStartCommand() that sends the intent to the work queue and then to your onHandleIntent() implementation.

All this adds up to the fact that all you need to do is implement onHandleIntent() to do the work provided by the client. (Though, you also need to provide a small constructor for the service.)



[ サンプルプログラム ]

 
アプリの見た目も、その動作も、前回の記事で書いた「Android: Serviceの起動と停止のサンプル」とほとんど変わらない。

前回のサンプルとの違いは、

前回のWorkerスレッドはMessageQueueを持たない単なるスレッドだったが、今回はMessageQueueと、そのキューを順番に処理するLooperを持つ。
なので、たとえば3回続けて起動ボタンを押すと3回分のIntentがMessageQueueにキューイングされてWorkerスレッドによって順に処理され、3つのIntentの処理が終わった時点でサービスが終了する。その処理中にサービス停止のIntentを送っても、Workerスレッドは停止しない。

したがって、サービスの停止ボタンはほとんど用をなさないのだが、上記動作の確認のために残してあります。

また、MessageQueueの状態(キューにいくつのメッセージが溜まっているかなど)をLooperのメソッドdump()でダンプできるようなのですが、これは別の機会に試すことにして、まずAndoridの全体像を把握するために、次のトピックに進もうと思います。

参考:
IntentService.javaのソースコードを見ると、MessageQueueとLooperの生成、サービス処理ルーチンonHandleIntent()の呼び出しに関する部分は次のように書かれています。

 46 public abstract class IntentService extends Service {
 47     private volatile Looper mServiceLooper;
 48     private volatile ServiceHandler mServiceHandler;
 49     private String mName;
 50     private boolean mRedelivery;
 51 
 52     private final class ServiceHandler extends Handler {
 53         public ServiceHandler(Looper looper) {
 54             super(looper);
 55         }
 56 
 57         @Override
 58         public void handleMessage(Message msg) {
 59             onHandleIntent((Intent)msg.obj);
 60             stopSelf(msg.arg1);
 61         }
 62     }

 94     @Override
 95     public void onCreate() {

100         super.onCreate();
101         HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
102         thread.start();
103 
104         mServiceLooper = thread.getLooper();
105         mServiceHandler = new ServiceHandler(mServiceLooper);
106     }



[このサンプルについて]

ControlServiceSample2Activity.java
  => 前回の記事のものと同じ

ServiceSample2.java

http://developer.android.com/guide/topics/fundamentals/services.html のExtending the IntentService classのサンプルにも書かれているように、サービスのためのクラスの定義と、onHandleIntent(){}にサービスの処理を書くだけでほとんどよい。
   => "That's all you need: a constructor and an implementation of onHandleIntent()."

 10行目 public class ServiceSample2 extends IntentService {

サービスの処理(例:一定間隔でカウントアップする)

 60行目   @Override
 61行目   protected void onHandleIntent(Intent intent) {
 62行目       // Normally we would do some work here, like download a file.
 63行目       // For our sample, we just sleep for 5 seconds.
 64行目           try {
 65行目                         int count = 0;
 66行目                         do{
 67行目                                 count += 1;
 68行目                         String text = String.valueOf(count);
 69行目                         mHandler.post(new ShowToast(ServiceSample2.this,count));
 70行目                         Log.v("ServiceStatus", "count="+text);
 71行目                                 Thread.sleep(5000); // interval is 5 sec.
 72行目                         } while (count != ServiceSample2.UpperLimit);
 73行目                 } catch (InterruptedException e) {
 74行目                         e.printStackTrace();
 75行目                 }
 76行目   }



[サンプルのコード]
ControlServiceSample2Activity.java
  => 前回の記事のものと同じ

ServiceSample2.java

package jp.knowd.ControlServiceSample2;

import android.content.Intent;
import android.widget.Toast;
import android.app.IntentService;
import android.util.Log;
import android.content.Context;
import android.os.Handler;

public class ServiceSample2 extends IntentService {
	
	public class ShowToast implements Runnable {

		private Context mContext;
		private int mCount;

		public ShowToast(Context context, int count) {
			mContext = context;
			mCount = count;
		}

		@Override
		public void run() {
			String text = String.valueOf(mCount);
			Toast.makeText(mContext, text, Toast.LENGTH_SHORT).show();
		}
	}
	
	public static int UpperLimit;
	private Handler mHandler;
	
  /** 
   * A constructor is required, and must call the super IntentService(String)
   * constructor with a name for the worker thread.
   */
  public ServiceSample2() {
      super("Worker Thread of ServiceSample2");
		mHandler = new Handler();
  }
  
  @Override
  public int onStartCommand(Intent intent, int flags, int startId) {
      Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
      Log.i("ServiceStatus", "ServiceSample2 started!!");    
      return super.onStartCommand(intent,flags,startId);
  }
  
  @Override
  public void onDestroy() {
      super.onDestroy();
      Toast.makeText(this, "Service stopping", Toast.LENGTH_SHORT).show();
      Log.w("ServiceStatus", "ServiceSample2 terminated!!");
  } 
  
  /**
   * The IntentService calls this method from the default worker thread with
   * the intent that started the service. When this method returns, IntentService
   * stops the service, as appropriate.
   */
  @Override
  protected void onHandleIntent(Intent intent) {
      // Normally we would do some work here, like download a file.
      // For our sample, we just sleep for 5 seconds.
	  try {
			int count = 0;
			do{
				count += 1;
		    	String text = String.valueOf(count);
		    	mHandler.post(new ShowToast(ServiceSample2.this,count));
		    	Log.v("ServiceStatus", "count="+text);
				Thread.sleep(5000); // interval is 5 sec.
			} while (count != ServiceSample2.UpperLimit);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
  }
}

AndroidManifest.xml
  => 前回の記事のものと同じ