LoginSignup
11
11

More than 5 years have passed since last update.

アプリ内で通知からスタックした複数画面を開くときに裏のActivityを残す

Posted at

自身のアプリを起動中に、プッシュ通知などでnotificationを表示したとき、何も考えずにTaskStackBuilderを使ってスタックした複数のActivityを起動すると、それまでに開いていた画面がクリアされてしまいます。例えば、

ActivityA | ActivityB | ActivityC

と画面遷移していたときに通知が来て、 ActivityDとActivityEを起動したいとき、

ActivityD | ActivityE | ActivityA | ActivityB | ActivityC

となってほしいところが 

ActivityD | ActivityE

のみになってしまい、適切にupアクションやparentActivityを設定していないと全ての画面が破棄されてしまいます。

アプリを開いていて、メッセージ機能などリアルタイムに別画面へ遷移をしたら下の画面が消えてしまうと困る場合の対処です。

ポイントはTaskStackBuildergetIntents

通常のこのような実装をすると思います。

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は全て破棄されてしまいます。
TaskStackBuildergetIntentの実装は以下のようになっています。

TaskStackBuilder.java#getIntents
/**
 * 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

サンプルを作ったので触ってみると動きがわかります。(名前ミスったかな…

PendingIntentCustomSample


ActivityスタックやIntentのフラグについてよくわからない方はyanzmさんの記事がわかりやすいと思います。

Android Activity, Task, Stack, Launch mode | Y.A.M の 雑記帳

Notificationについて詳しい記事はYahoo!の以下の記事がわかりやすいと思います。

LollipopでのNotification | Yahoo! Tech blog

11
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
11