PendingIntentのフラグ設定による振る舞いの違い


Pocket

AlarmManagerを使って指定時間に処理を呼ぶときや、Notificationで出した通知をクリックした時に画面遷移させるときにPendingIntentを使用すると思います。
で、PendingIntentオブジェクト生成するときに設定するフラグですが、今までなんとなくFLAG_UPDATE_CURRENTを使用していましたが、他のフラグ使うとどうなるのか?

ドキュメントを見てもいまいち解らなかったので動かして調べて見ました。

ドキュメントの説明は下記の通りです。

フラグ 説明
FLAG_CANCEL_CURRENT Flag indicating that if the described PendingIntent already exists, the current one should be canceled before generating a new one.
FLAG_NO_CREATE Flag indicating that if the described PendingIntent does not already exist, then simply return null instead of creating it.
FLAG_ONE_SHOT Flag indicating that this PendingIntent can be used only once.
FLAG_UPDATE_CURRENT Flag indicating that if the described PendingIntent already exists, then keep it but replace its extra data with what is in this new Intent.

 

テスト方法

テスト方法は、
1.ログを出力するだけのHogeIntentServiceを呼び出すIntentを生成
2.putExtra()で任意のIDを設定
3.Pending.getService()でPendingIntentオブジェクトを生成
4.10秒後に実行されるようAlarmManagerへセットする
5.1~4をIDを変えて3回おこない、どの順でAlarmManagerから呼ばれるか確認する

実行したコードは下記の通りです。

HogeIntentService.java

public class HogeIntentService extends IntentService {

	public HogeIntentService() {
		super("HogeIntentService");
		// TODO Auto-generated constructor stub
	}

	@Override
	protected void onHandleIntent(Intent intent) {
		Log.d("Hoge", intent.getAction() + " : extra=" + intent.getExtras().getInt("extra") + " " + intent.hashCode());
	}

}

MainActivity.java※抜粋

	private void runTest0() {
		Log.d("PendingFlagTest", "runTest0");
		setTestIntent(0, PendingIntent.FLAG_CANCEL_CURRENT);
		setTestIntent(1, PendingIntent.FLAG_CANCEL_CURRENT);
		setTestIntent(2, PendingIntent.FLAG_CANCEL_CURRENT);		
	}

	private void runTest1() {
		Log.d("PendingFlagTest", "runTest1");
		setTestIntent(3, PendingIntent.FLAG_NO_CREATE);
		setTestIntent(4, PendingIntent.FLAG_NO_CREATE);
		setTestIntent(5, PendingIntent.FLAG_NO_CREATE);		
	}
	
	private void runTest2() {
		Log.d("PendingFlagTest", "runTest2");
		setTestIntent(6, PendingIntent.FLAG_ONE_SHOT);
		setTestIntent(7, PendingIntent.FLAG_ONE_SHOT);
		setTestIntent(8, PendingIntent.FLAG_ONE_SHOT);		
	}
	
	private void runTest3() {
		Log.d("PendingFlagTest", "runTest3");
		setTestIntent(9, PendingIntent.FLAG_UPDATE_CURRENT);
		setTestIntent(10, PendingIntent.FLAG_UPDATE_CURRENT);
		setTestIntent(11, PendingIntent.FLAG_UPDATE_CURRENT);		
	}
	
	private void setTestIntent(int id, int flag) {		
		Intent intent = new Intent(MainActivity.this, HogeIntentService.class);
		intent.setAction("TestIntent");
		intent.putExtra("extra", id);
		PendingIntent sender = PendingIntent.getService(this.getApplicationContext(), 0, intent, flag);
		if (sender == null) {
			Log.d("createIntent", "Not create:" + id);
			return;
		}
		
		Log.d("createIntent", "create TestIntent@" + sender.hashCode() + " : extra=" + id);		
		Calendar calendar = Calendar.getInstance();
		calendar.add(Calendar.SECOND, 10);
		AlarmManager am = (AlarmManager)this.getSystemService(Context.ALARM_SERVICE);
		am.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), sender);
	}

テストはAndroid4.0.4端末で実施しました。

 

結果

FLAG_CANCEL_CURRENT

・ログ出力結果

06-21 23:11:08.276: D/createIntent(9328): create TestIntent@1097378296 : extra=0
06-21 23:11:08.286: D/createIntent(9328): create TestIntent@1097379424 : extra=1
06-21 23:11:08.286: D/createIntent(9328): create TestIntent@1097380552 : extra=2
06-21 23:11:18.365: D/Hoge(9328)        : TestIntent : extra=2 1097384424

@の後の数字は、getService()で生成したPendingIntentオブジェクトのハッシュ値です。
ログを見るとそれぞれ別のオブジェクトが生成されますが、AlarmManagerから呼ばれたのは最後にセットしたPendingIntentだけでした。

・AlarmManagerのアラーム登録状態

  RTC_WAKEUP #2: Alarm{425d5ac8 type 0 com.example.pendingintentflag}
    type=0 when=+2s837ms repeatInterval=0 count=0
    operation=PendingIntent{425df340: PendingIntentRecord{422733d0 com.example.pendingintentflag startService}}
  RTC_WAKEUP #1: Alarm{41f2be78 type 0 com.example.pendingintentflag}
    type=0 when=+2s832ms repeatInterval=0 count=0
    operation=PendingIntent{41dc4130: PendingIntentRecord{422e2410 com.example.pendingintentflag startService}}
  RTC_WAKEUP #0: Alarm{423499c0 type 0 com.example.pendingintentflag}
    type=0 when=+2s822ms repeatInterval=0 count=0
    operation=PendingIntent{4238abd8: PendingIntentRecord{42356d88 com.example.pendingintentflag startService}}

AlarmManagerの状態をダンプしてみると、アラームは3つとも実行されていました。

#内2つのアラームは時間になったら何もせずに消滅したようですが・・・そこら辺の処理がとうなっているのかまでは今回は追ってないです。PendingIntentの参照先がNullってると何もしないのかな?Exceptionとかないのかな?

 

FLAG_NO_CREATE

・ログ出力結果

06-21 23:45:04.612: D/createIntent(17481): Not create:3
06-21 23:45:04.622: D/createIntent(17481): Not create:4
06-21 23:45:04.622: D/createIntent(17481): Not create:5

ログを確認すると、3回ともNullになっていました。ドキュメント通りの振る舞いですね。

ちなみに、Test0を実行した後に再度Test1を実行すると下記のような振る舞いになりました。
・ログ出力結果

//Test0
06-21 23:45:24.021: D/createIntent(17481): create TestIntent@1096908960 : extra=0
06-21 23:45:24.031: D/createIntent(17481): create TestIntent@1096892256 : extra=1
06-21 23:45:24.031: D/createIntent(17481): create TestIntent@1096868672 : extra=2
06-21 23:45:34.061: D/Hoge(17481)        : TestIntent : extra=2 1096829784
//Test1
06-21 23:46:02.749: D/createIntent(17481): create TestIntent@1096868672 : extra=3
06-21 23:46:02.749: D/createIntent(17481): create TestIntent@1096868672 : extra=4
06-21 23:46:02.759: D/createIntent(17481): create TestIntent@1096868672 : extra=5
06-21 23:46:12.809: D/Hoge(17481)        : TestIntent : extra=2 1096765080

Test0の3回めのPendingIntentオブジェクトを使いまわしているようで、putExtraで設定した値も反映されないようです。

・AlarmManagerのアラーム登録状態

  RTC_WAKEUP #0: Alarm{421c2258 type 0 com.example.pendingintentflag}
    type=0 when=+739ms repeatInterval=0 count=0
    operation=PendingIntent{42536588: PendingIntentRecord{423b6920 com.example.pendingintentflag startService}}

セットされるアラームも1つのみ。

 

FLAG_ONE_SHOT

・ログ出力結果

06-21 23:50:41.291: D/createIntent(18331): create TestIntent@1097151936 : extra=6
06-21 23:50:41.311: D/createIntent(18331): create TestIntent@1097151936 : extra=7
06-21 23:50:41.321: D/createIntent(18331): create TestIntent@1097151936 : extra=8
06-21 23:50:51.381: D/Hoge(18331)        : TestIntent : extra=6 1097159224

一番始めに生成したPendingIntentのみが有効になりました。

・AlarmManagerのアラーム登録状態

  RTC_WAKEUP #0: Alarm{4247e440 type 0 com.example.pendingintentflag}
    type=0 when=+1s569ms repeatInterval=0 count=0
    operation=PendingIntent{42483de8: PendingIntentRecord{4246a540 com.example.pendingintentflag startService}}

セットされるアラームは1つ。

 

FLAG_UPDATE_CURRENT

・ログ出力結果

06-21 23:54:49.983: D/createIntent(18797): create TestIntent@1097148352 : extra=9
06-21 23:54:50.003: D/createIntent(18797): create TestIntent@1097148352 : extra=10
06-21 23:54:50.013: D/createIntent(18797): create TestIntent@1097148352 : extra=11
06-21 23:55:00.103: D/Hoge(18797)        : TestIntent : extra=11 1097155664

始めに生成したオブジェクトのままputExtraの値だけ更新されました。まさにUPDATEといった振る舞い。

・AlarmManagerのアラーム登録状態

  RTC_WAKEUP #0: Alarm{4245da28 type 0 com.example.pendingintentflag}
    type=0 when=+7s895ms repeatInterval=0 count=0
    operation=PendingIntent{41f23c90: PendingIntentRecord{41e3aa90 com.example.pendingintentflag startService}}

セットされるアラームは1つ。

 

FLAG_ONE_SHOT後のFLAG_CANCEL_CURRENT

・ログ出力結果

//Test2
06-22 00:04:11.942: D/createIntent(19613): create TestIntent@1097134440 : extra=6
06-22 00:04:11.952: D/createIntent(19613): create TestIntent@1097134440 : extra=7
06-22 00:04:11.952: D/createIntent(19613): create TestIntent@1097134440 : extra=8
//Test0
06-22 00:04:13.783: D/createIntent(19613): create TestIntent@1096894136 : extra=0
06-22 00:04:13.783: D/createIntent(19613): create TestIntent@1096761328 : extra=1
06-22 00:04:13.783: D/createIntent(19613): create TestIntent@1096788688 : extra=2
06-22 00:04:22.001: D/Hoge(19613)        : TestIntent : extra=6 1096894808
06-22 00:04:23.843: D/Hoge(19613)        : TestIntent : extra=2 1096836520

ONE_SHOT後にCANCEL_CURRENTを実行してみましたが、ONE_SHOTはキャンセルされませんでした。

・AlarmManagerのアラーム登録状態

  RTC_WAKEUP #3: Alarm{41fd4428 type 0 com.example.pendingintentflag}
    type=0 when=+6s422ms repeatInterval=0 count=0
    operation=PendingIntent{41fd4418: PendingIntentRecord{41fd4378 com.example.pendingintentflag startService}}
  RTC_WAKEUP #2: Alarm{41fd3f68 type 0 com.example.pendingintentflag}
    type=0 when=+6s413ms repeatInterval=0 count=0
    operation=PendingIntent{41fd3f58: PendingIntentRecord{41fd3eb8 com.example.pendingintentflag startService}}
  RTC_WAKEUP #1: Alarm{41fd3aa8 type 0 com.example.pendingintentflag}
    type=0 when=+6s401ms repeatInterval=0 count=0
    operation=PendingIntent{41fd3a98: PendingIntentRecord{42512528 com.example.pendingintentflag startService}}
  RTC_WAKEUP #0: Alarm{41c6d1d0 type 0 com.example.pendingintentflag}
    type=0 when=+4s931ms repeatInterval=0 count=0
    operation=PendingIntent{41c6d1c0: PendingIntentRecord{425b5710 com.example.pendingintentflag startService}}

アラームは4つ。

 

FLAG_UPDATE_CURRENT後のFLAG_CANCEL_CURRENT

・ログ出力結果

//Test3
06-22 00:19:31.319: D/createIntent(21264): create TestIntent@1097162112 : extra=9
06-22 00:19:31.319: D/createIntent(21264): create TestIntent@1097162112 : extra=10
06-22 00:19:31.329: D/createIntent(21264): create TestIntent@1097162112 : extra=11
//Test0
06-22 00:19:33.110: D/createIntent(21264): create TestIntent@1096937016 : extra=0
06-22 00:19:33.110: D/createIntent(21264): create TestIntent@1096865704 : extra=1
06-22 00:19:33.120: D/createIntent(21264): create TestIntent@1096855536 : extra=2
06-22 00:19:43.140: D/Hoge(21264)        : TestIntent : extra=2 1096809440

UPDAET_CURRENTはCANCEL_CURRENTでキャンセルされました。

アラームは3つ。

 

FLAG_ONE_SHOT後のFLAG_UPDATE_CURRENT

・ログ出力結果

//Test2
06-22 00:13:29.205: D/createIntent(20569): create TestIntent@1097137752 : extra=6
06-22 00:13:29.235: D/createIntent(20569): create TestIntent@1097137752 : extra=7
06-22 00:13:29.235: D/createIntent(20569): create TestIntent@1097137752 : extra=8
//Test3
06-22 00:13:31.087: D/createIntent(20569): create TestIntent@1096913192 : extra=9
06-22 00:13:31.097: D/createIntent(20569): create TestIntent@1096913192 : extra=10
06-22 00:13:31.117: D/createIntent(20569): create TestIntent@1096913192 : extra=11
06-22 00:13:39.285: D/Hoge(20569)        : TestIntent : extra=6 1096891120
06-22 00:13:41.157: D/Hoge(20569)        : TestIntent : extra=11 1096833288

ONE_SHOTとUPDATE_CURRENTで更新されれず、別々に処理されました。

アラームは2つ。

 

FLAG_CANCEL_CURRENT後のFLAG_NO_CREATE

・ログ出力結果

//Test0
06-22 00:45:16.366: D/createIntent(23336): create TestIntent@1097221528 : extra=0
06-22 00:45:16.366: D/createIntent(23336): create TestIntent@1097222656 : extra=1
06-22 00:45:16.376: D/createIntent(23336): create TestIntent@1097223784 : extra=2
//Test1
06-22 00:45:17.877: D/createIntent(23336): create TestIntent@1097223784 : extra=3
06-22 00:45:17.877: D/createIntent(23336): create TestIntent@1097223784 : extra=4
06-22 00:45:17.887: D/createIntent(23336): create TestIntent@1097223784 : extra=5
06-22 00:45:27.997: D/Hoge(23336)        : TestIntent : extra=2 1097295536

 

FLAG_ONE_SHOT後のFLAG_NO_CREATE

・ログ出力結果

//Test2
06-22 00:37:13.905: D/createIntent(23336): create TestIntent@1097158136 : extra=6
06-22 00:37:13.925: D/createIntent(23336): create TestIntent@1097158136 : extra=7
06-22 00:37:13.935: D/createIntent(23336): create TestIntent@1097158136 : extra=8
//Test1
06-22 00:37:15.647: D/createIntent(23336): Not create:3
06-22 00:37:15.657: D/createIntent(23336): Not create:4
06-22 00:37:15.667: D/createIntent(23336): Not create:5
06-22 00:37:23.985: D/Hoge(23336)        : TestIntent : extra=6 1096917672

ONE_SHOT後のNO_CREATEはNullになった。
最初のPendingIntentが返ってくると思ったけど、予想外にNullでした。

今回調べたのは以上です。

あとがき

今まで漠然とAlarmManagerやNotificationを使用する時にFLAG_UPDATE_CURRENTを使用していましたが、今回調べてみてそれぞれの使い所がなんとなく分かった気がします。

・一発を確実に出す場合=FLAG_ONE_SHOT
・情報を更新しつつ何度も出す場合=FLAG_CANCEL_CURRENT(丸っと更新) or FLAG_UPDATE_CURRENT(一部更新)
・直前のやつを再利用(リピート)する場合=FLAG_NO_CREATE

今回使用したコードは下に置いておきますね。
https://github.com/workpiles/PendingIntentFlagTest

 
 

Leave a Comment

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です