2011年7月15日金曜日

定期的にIntentServiceを起動する

IntentServiceは、
Activityからの呼び出し時にAlarmManagerと併用することにより、
定期的な呼び出しを行うことができる。

AlarmManagerは、引数にPendingIntentを要求するため、
PendingIntentを取得する必要がある。
ただ、特に細かい設定はなく、サービスのIntentを型変換しているような感じだ。


■定期的に動くIntentServiceを作る(AlarmManaerを利用)
1)新規のAndroidプロジェクトを以下のように作成する。
「プロジェクト名」:AlarmManagerIntentService
「ビルドターゲット」:Android 1.6
「パッケージ名」:jp.co.example.alarmmanagerintentservice
「アクティビティ名」:AlarmManagerIntentServiceActivity

2)サービスクラスを作成する。
プロジェクトのsrcのパッケージ配下に、『MyIntentService』クラスを作成
ソース内容は以下の通り。

基本的には、MyIntentServiceクラスをIntentServiceクラスの継承クラスとする。
Seriviceの継承とは異なり、コンストラクターのみ強制オーバーライドであった。

Eclipseのコードアシスタントでは、第一引数ありのコンストラクタを生成してくれる、
しかし、
ActivityからAlarmManagerを利用して呼ぶ場合、引数なしのコンストラクタが必要である。

onHandleIntentメソッドには、ログを入れた。
これは、onHandleIntentがどのスレッドで動くか調べるためだ。
結果、onHandleIntentは、時間毎に新規スレッドが生まれる形で動作した。

以下のコードにおけるIntentServiceは、
Activityと同プロセスだが、別スレッドで動作している。

package jp.co.example.alarmmanagerintentservice;

import android.app.IntentService;
import android.content.Intent;
import android.util.Log;

public class MyIntentService extends IntentService {

 private final String TAG = this.getClass().getSimpleName();

 public MyIntentService(String name) {
  super(name);
  // TODO 自動生成されたコンストラクター・スタブ
  Log.d(TAG, Thread.currentThread().getStackTrace()[2].getMethodName());
 }

 public MyIntentService() {
  super("MyIntentService");
  // TODO 自動生成されたコンストラクター・スタブ
  Log.d(TAG, Thread.currentThread().getStackTrace()[2].getMethodName());
 }

 @Override
 protected void onHandleIntent(Intent intent) {
  // TODO 自動生成されたメソッド・スタブ
  Log.d(TAG, Thread.currentThread().getStackTrace()[2].getMethodName());

 }

}

3)画面にスタートボタン、ストップボタンを作る
res\layout\main.xmlに、ボタンを2つ作成。
ソースは以下のようになる。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <Button android:text="Alarm Start" android:id="@+id/start_button" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
    <Button android:text="Alarm Stop" android:id="@+id/stop_button" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
</LinearLayout>


4)サービスをスタート/ストップするアクティビティを作る
以下の処理を行っている。
スタートボタン押下処理をstartAlarmServiceメソッドとした。
ストップボタン押下処理をstopAlarmServiceメソッドとした。
詳細は、ソースコードコメントに記載する。

全体のコードは以下の通り。
package jp.co.example.alarmmanagerintentservice;

import android.app.Activity;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class AlarmManagerIntentServiceActivity extends Activity {

 private final String TAG = this.getClass().getSimpleName();

 Button alarmStartButton;
 Button alarmStopButton;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, Thread.currentThread().getStackTrace()[2].getMethodName());
        setContentView(R.layout.main);

        findViews();

        alarmStartButton.setOnClickListener(new OnClickListener() {

   public void onClick(View v) {
    // TODO 自動生成されたメソッド・スタブ
    startAlarmService();
   }
  });

        alarmStopButton.setOnClickListener(new OnClickListener() {

   public void onClick(View v) {
    // TODO 自動生成されたメソッド・スタブ
    stopAlarmService();
   }
  });
    }

    protected void findViews() {
     alarmStartButton = (Button)findViewById(R.id.start_button);
     alarmStopButton = (Button)findViewById(R.id.stop_button);
    }

    protected void startAlarmService() {
     Log.d(TAG, Thread.currentThread().getStackTrace()[2].getMethodName());
     Context context = getBaseContext();
     // サービスのIntentを生成
     Intent intent = new Intent(context, MyIntentService.class);
     // PendingIntentを取得
     PendingIntent pendingIntent = PendingIntent.getService(
       context,
       -1,  // requestCode is currently not used
       intent, // 起動するサービスのIntent
       PendingIntent.FLAG_UPDATE_CURRENT
        //flags: FLAG_CANCEL_CURRENT :過去の設定をキャンセルし現在の設定を使う。
        //   FLAG_NO_CREATE  :存在してなければnullを返す。新規作成しない。
        //   FLAG_ONE_SHOT  :一度だけ使用できる。
        //   FLAG_UPDATE_CURRENT :過去の設定があればそれを使う。再設定しない。
       );
     AlarmManager alarmManager = (AlarmManager)context.getSystemService(ALARM_SERVICE);
     alarmManager.setInexactRepeating(
       AlarmManager.RTC,
        // type:
        // ELAPSED_REALTIME  :SystemClock.elapsedRealtime() によるアラーム時間 (スリープを含む起動してからの時間)
        // ELAPSED_REALTIME_WAKEUP :ELAPSED_REALTIME と同様だが、スリープしている時は起きる
        // INTERVAL_DAY   :一日間隔
        // INTERVAL_FIFTEEN_MINUTES:15分間隔
        // INTERVAL_HALF_DAY  :半日間隔
        // INTERVAL_HALF_HOUR  :30分間隔
        // INTERVAL_HOUR   :一時間間隔
        // RTC      :System.currentTimeMillis() によるアラーム時間 (UTC)
        // RTC_WAKEUP    :RTC と同様だが、スリープしている時は起きる
       System.currentTimeMillis(), // triggerAtTime
       5000,      // interval
       pendingIntent    // operation
       );
    }

    protected void stopAlarmService() {
     Log.d(TAG, Thread.currentThread().getStackTrace()[2].getMethodName());
     Context context = getBaseContext();
     // サービスのIntentを生成
     Intent intent = new Intent(context, MyIntentService.class);
     // PendingIntentを取得
     PendingIntent pendingIntent = PendingIntent.getService(
       context,
       -1,  // requestCode is currently not used
       intent,
       PendingIntent.FLAG_UPDATE_CURRENT
       );
     AlarmManager alarmManager = (AlarmManager)context.getSystemService(ALARM_SERVICE);
     alarmManager.cancel(pendingIntent);
    }

}

5)マニフェストでサービス許可する
AndroidManifest.xmlに<service>タグにサービスクラス名を指定して、以下のようにサービスを許可する。
<application android:icon="@drawable/icon" android:label="@string/app_name">
 (略)
 <service android:name=".MyIntentService"/>
</application>

0 件のコメント:

コメントを投稿