自身のアプリを起動中に、プッシュ通知などでnotificationを表示したとき、何も考えずにTaskStackBuilder
を使ってスタックした複数のActivity
を起動すると、それまでに開いていた画面がクリアされてしまいます。例えば、
ActivityA | ActivityB | ActivityC
と画面遷移していたときに通知が来て、 ActivityDとActivityEを起動したいとき、
ActivityD | ActivityE | ActivityA | ActivityB | ActivityC
となってほしいところが
ActivityD | ActivityE
のみになってしまい、適切にupアクションやparentActivity
を設定していないと全ての画面が破棄されてしまいます。
アプリを開いていて、メッセージ機能などリアルタイムに別画面へ遷移をしたら下の画面が消えてしまうと困る場合の対処です。
ポイントはTaskStackBuilder
のgetIntents
通常のこのような実装をすると思います。
TaskStackBuilder taskStackBuilder = TaskStackBuilder.create(context.getApplicationContext());
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(getApplicationContext());
notificationBuilder.setContentTitle("normal").setContentText("nomal");
notificationBuilder.setAutoCancel(true);
Intent intent = new Intent(context, NextActivity.class);
taskStackBuilder.addNextIntent(intent);
PendingIntent pi = PendingIntent.getActivities(context, 0, taskStackBuilder.getIntents(), PendingIntent.FLAG_CANCEL_CURRENT);
notificationBuilder.setFullScreenIntent(pi, true);
notificationBuilder.setContentIntent(pi);
NotificationManager notificationManager = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
notificationManager.notify(0, notificationBuilder.build());
この場合だと下のActivityは全て破棄されてしまいます。
TaskStackBuilder
のgetIntent
の実装は以下のようになっています。
/**
* Return an array containing the intents added to this builder. The intent at the
* root of the task stack will appear as the first item in the array and the
* intent at the top of the stack will appear as the last item.
*
* @return An array containing the intents added to this builder.
*/
public Intent[] getIntents() {
Intent[] intents = new Intent[mIntents.size()];
if (intents.length == 0) return intents;
intents[0] = new Intent(mIntents.get(0)).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
IntentCompat.FLAG_ACTIVITY_CLEAR_TASK |
IntentCompat.FLAG_ACTIVITY_TASK_ON_HOME);
for (int i = 1; i < intents.length; i++) {
intents[i] = new Intent(mIntents.get(i));
}
return intents;
}
つまりgetIntents
するとTaskStackBuilder
に積まれた一番下のIntent
の起動フラグがFLAG_ACTIVITY_CLEAR_TASK
になってしまいます。そのせいで通知から起動すると下の画面が全て破棄されます。よってここを変えれば下の画面を残したまま通知から別な画面を起動できます。
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(getApplicationContext());
notificationBuilder.setContentTitle("normal").setContentText("nomal");
notificationBuilder.setAutoCancel(true);
Intent intent = new Intent(context, NextActivity.class);
Intent[] intents = new Intent[1];
intents[0] = intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//PendingIntent pi = PendingIntent.getActivities(context, 0, taskStackBuilder.getIntents(), PendingIntent.FLAG_CANCEL_CURRENT);
PendingIntent pi = PendingIntent.getActivities(context, 0, intents, PendingIntent.FLAG_CANCEL_CURRENT);
notificationBuilder.setFullScreenIntent(pi, true);
notificationBuilder.setContentIntent(pi);
NotificationManager notificationManager = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
notificationManager.notify(0, notificationBuilder.build());
やり方はこれ以外にもあると思いますが、重要なのは
getIntents
から取得したIntent
配列で0番目のフラグを変えればいい(もしくは自分で配列を作って渡す)ということです。
以上です。
sample
サンプルを作ったので触ってみると動きがわかります。(名前ミスったかな…
ActivityスタックやIntentのフラグについてよくわからない方はyanzmさんの記事がわかりやすいと思います。
Android Activity, Task, Stack, Launch mode | Y.A.M の 雑記帳
Notificationについて詳しい記事はYahoo!の以下の記事がわかりやすいと思います。