戌印-INUJIRUSHI- (Androidあれこれ)

Androidのプログラミングをメインにしてます。記事に貼られたソースコードはダブルクリックすることで行番号をはずしてコピーすることができます。

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
10

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

指定時間に処理を実行する : AlarmManager

Android では指定された時間、または、一定時間毎に処理を実行するために AlarmManager が用意されています。

例えば、10秒毎にサービスを実行するには以下のように記述します。
// 実行するサービスを指定する
PendingIntent pendingIntent = PendingIntent.getService(context, 0,
new Intent(context, SampleService.class),
PendingIntent.FLAG_UPDATE_CURRENT);

// 10秒毎にサービスの処理を実行する
AlarmManager am = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
am.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime(), 10 * 1000, pendingIntent);


■ AlarmManager の注意点
①AlarmManager の実行周期が長いと時間が遅れる(数時間で2~3分?)
②AlermManager は cancel しなくても以下の時に終了する
 ・デバイスの電源をオフにする(再起動する)
 ・アプリをアップデートする
 ・アプリをアンインストールする

この問題は BroadcastReceiver クラスを使い、再実行することで回避することが出来ます。

①の時間が遅れる問題は時間が自動設定されているときに発生するので、システム時間が変更された時に発生する broadcast を受け取るようにします。
<receiver
android:name=".SampleBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.TIMEZONE_CHANGED" />
<action android:name="android.intent.action.TIME_SET" />
<action android:name="android.intent.action.DATE_CHANGED" />
</intent-filter>
</receiver>

②はデバイス起動時とアプリ更新時に発生する broadcast を受け取ってください。アンインストール時はアプリ自体がなくなるので指定する必要はありません。
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

<receiver
android:name=".SampleBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.PACKAGE_REPLACED" />
<data
android:path="jp.inujirushi.android.sample"
android:scheme="package" />
</intent-filter>
</receiver>
起動時に broadcast を受け取るには android.permission.RECEIVE_BOOT_COMPLETED 権限が必要です。
そして、アプリ更新時には <data android:scheme="package" /> が必要です。<data> タグに android:path を指定することで自身のアプリのみを対象にできる…らしいのですが android:path を指定しても他のアプリ更新時に broadcast が投げられてしまいました。BroadCast クラス側で path のチェックをする必要があるようです。
(HTC J ISW13HT で確認)



以上を踏まえ、10秒毎にログを出力する簡単なアプリを作ってみました。
AlermManager のON/OFFの管理は行っていません。アプリが1度でも起動したら常にONになります。

なお、Android 3.1 (API12 / Haneycomb) 以降、アプリが1度も実行されていない場合は STOP 状態となり broadcast も受け取りません。そのためインストール時に自動で開始させることはできません。なので「1度でも起動したら」という表記を用いています。

SampleService.java
package jp.inujirushi.android.sample;

import android.app.AlarmManager;
import android.app.IntentService;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.SystemClock;
import android.util.Log;

public class SampleService extends IntentService {

private static final String TAG = "SampleService";

public SampleService() {
super(TAG);
}

@Override
protected void onHandleIntent(Intent intent) {
Log.d(TAG, "time:" + SystemClock.elapsedRealtime());
}

/**
* サービスを処理する AlarmManager を開始する。
*
* @param context
*/
public static void startAlarm(Context context) {
// 実行するサービスを指定する
PendingIntent pendingIntent = PendingIntent.getService(context, 0,
new Intent(context, SampleService.class),
PendingIntent.FLAG_UPDATE_CURRENT);

// 10秒毎にサービスの処理を実行する
AlarmManager am = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
am.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime(), 10 * 1000, pendingIntent);
}
}

SampleActivity.java
package jp.inujirushi.android.sample;

import android.app.Activity;
import android.os.Bundle;

public class SampleActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_layout);

// AlarmManager を開始する
SampleService.startAlarm(getApplicationContext());
}
}

SampleBroadcastReceiver.java
package jp.inujirushi.android.sample;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

public class SampleBroadcastReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
// 他のアプリ更新時は対象外とする
if (intent.getAction().equals(Intent.ACTION_PACKAGE_REPLACED)) {
if (!intent.getDataString().equals(
"package:" + context.getPackageName())) {
return;
}
}

// AlarmManager を開始する
SampleService.startAlarm(context);
}
}

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest package="jp.inujirushi.android.sample"
android:versionCode="1"
android:versionName="1.0" xmlns:android="http://schemas.android.com/apk/res/android">

<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="18" />

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".SampleActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<receiver
android:name=".SampleBroadcastReceiver"
android:process=":remote" >
<intent-filter>
<action android:name="android.intent.action.TIMEZONE_CHANGED" />
<action android:name="android.intent.action.TIME_SET" />
<action android:name="android.intent.action.DATE_CHANGED" />
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.PACKAGE_REPLACED" />
<data
android:path="jp.inujirushi.android.sample"
android:scheme="package" />
</intent-filter>
</receiver>

<service android:name=".SampleService"></service>
</application>

</manifest>
<receiver> タグにある android:process=":remote" は別プロセスで起動する場合に指定します。必須ではありません。


■参考サイト
Y.A.M の 雑記帳 - AlarmManager
素人のアンドロイドアプリ開発日記 - AlarmManagerを使う場合の注意点
rokuta96のAndroidアプリ開発 - アラーム3
kino's blog - Android Service を自動的に再起動する方法
スポンサーサイト

Comments

Leave a Comment


Body
プロフィール

とむ・やむくん

Author:とむ・やむくん
管理人について

Windows 7 / 64bit
Eclipse 4.2 Juno (日本語パッチ済)

スポンサーサイト
最新トラックバック
検索フォーム
ブロとも申請フォーム
QRコード
QR
Twitter
2013/01/04 19:00 カウント開始

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。