

通知タップでActivityが再描画される原因、PendingIntentのrequestCode被りだった
この記事は、構成と文章の整理にAIを使っています。
あくまで読みやすくするための補助で、内容そのものをAIにすべて任せて書いているわけではありませんが、ところどころ相談しながら整えています。
何が起きたか
サービス経由の通知はタップしてもActivityが再描画されないのに、別のローカル通知(サービスを経由しない方)をタップすると毎回再描画される。両方とも同じMainActivityを開くIntentで、PendingIntentのflagsも揃えてあった。
結論
原因はPendingIntentのrequestCodeが両方とも同じだったこと。
PendingIntentは中身のIntentそのもので区別されるわけじゃない。同一性はIntent.filterEquals()が見る要素(action, data, type, component, categories)とrequestCode、呼び出し元のパッケージ名の組み合わせで決まる。putExtraで入れたExtraは判定に入らない。
なので、起動先のComponent(今回はMainActivity)が同じでrequestCodeも同じだと、Extraの中身が違っていてもAndroidは「同じPendingIntent」とみなす。FLAG_UPDATE_CURRENTを付けていると、後から作られた方でその同一PendingIntentが上書きされる形になる。サービス側とローカル通知側、両方でrequestCode = 0を使っていたので、片方の挙動がもう片方に影響していた。
flowchart TD
A[PendingIntent.getActivity呼び出し] --> B{component/action/data等が一致?}
B -- 不一致 --> C[別のPendingIntentとして新規作成]
B -- 一致 --> D{requestCodeも一致?}
D -- 不一致 --> C
D -- 一致 --> E[同一PendingIntentとみなす]
E --> F[FLAG_UPDATE_CURRENTで中身が上書きされる]
対処
requestCodeを通知の種類ごとに分ける。
サービス側
PendingIntent.getActivity( context, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)ローカル通知側
PendingIntent.getActivity( context, 2, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)通知ごとにユニークなIDがあるなら、それをrequestCodeにそのまま使う手もある(requestCode = notificationId)。
再描画自体を抑えたい場合、そこはPendingIntentのflagsじゃなくてIntent側のflagsで制御する話になる。
val intent = Intent(context, MainActivity::class.java).apply { flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_REORDER_TO_FRONT}FLAG_ACTIVITY_SINGLE_TOPはActivityがスタックのトップにいる場合にそのまま再利用、FLAG_ACTIVITY_CLEAR_TOPは既存インスタンスを前面に持ってくる、FLAG_ACTIVITY_REORDER_TO_FRONTはタスク内の既存インスタンスを最前面に並び替える。Manifest側のlaunchModeをsingleTaskにしておくとonNewIntent()が呼ばれるようになる。
分かったこと
PendingIntentの同一性はIntentの中身全部じゃなくて、filterEquals()が見る一部の要素とrequestCodeで決まる。Extraが違うだけでは別扱いにならない。同じActivityに向けたPendingIntentを複数種類使うなら、requestCodeかcomponent/action等のfilterEquals対象のどちらかを意図的に変えないと、意図しない上書きが起きる。