singleTaskなのにonNewIntentが呼ばれない? App Links実装でハマった話
1. はじめに
Androidで App Links を実装する際、
Activity に launchMode="singleTask" を指定していました。
<activity
android:name=".HomeActivity"
android:launchMode="singleTask" />
singleTask を利用しているため、
- Activity が再利用される
- onNewIntent() が呼ばれる
という前提で実装を進めていました。
しかし、実際には想定と異なる挙動が発生しました。
本記事では、App Links 実装時に遭遇した singleTask と onNewIntent() に関する挙動について整理します。
2. 想定していた挙動
App Links でアプリが起動された場合、
既存の Activity が存在していれば onNewIntent() が呼ばれる想定でした。
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
handleDeepLink(intent)
}
つまり、singleTask = onNewIntent が呼ばれる という認識でした。
3. 実際に発生した問題
実際には、以下のようなケースが発生しました。
- onNewIntent() が呼ばれる場合がある
- onCreate() が呼ばれる場合がある
- 期待していた処理が実行されない場合がある
特に問題になったのは、
履歴から Activity が復元されたケースです。
この場合、App Links の再実行を想定していた処理が
意図せず再度動作してしまいました。
4. 原因
原因は singleTask の挙動を誤解していたことでした。
singleTask は、
- Activity の生成方法
- Task の扱い
に影響を与えますが、必ず onNewIntent() が呼ばれる ことを保証するものではありません。
Androidでは、
- Task の状態
- バックグラウンド復帰
- 履歴からの復元
- Intent の再配送
などによって挙動が変化します。
つまり、launchModeは挙動を完全に保証するものではない という点を理解する必要があります。
また、Androidではメモリ不足などにより、OSがバックグラウンドのProcessを終了させる場合があります。
このような Process Death が発生した場合、Activityは既存インスタンスとして再利用されず、復元時に onCreate() から再実行されます。
そのため、singleTask を指定していても、
常に onNewIntent() が呼ばれるとは限りません。
5. 実際に行った対応
今回のケースでは、
履歴から復元された Intent を判定し、
不要な再処理を行わないように対応しました。
if ((intent.flags and Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) {
return
}
また、
- onCreate()
- onNewIntent()
の両方で Intent を考慮するように実装を見直しました。
6. なぜハマりやすいのか
この問題が厄介なのは、
singleTask の説明だけを見ると、「既存 Activity が再利用される」 という印象を受けやすいためです。
しかし実際には、
どの lifecycle が呼ばれるか
まで単純には決まりません。
そのため、
singleTask にしたから安心
という実装は危険です。
7. まとめ
今回のポイントは以下です。
- singleTask は onNewIntent() を保証しない
- App Links では履歴復元を考慮する必要がある
- launchMode だけに依存した実装は危険
- Intent の状態を考慮した制御が重要
8. おわりに
Android の launchMode は便利ですが、
内部挙動を正しく理解していないと、
想定外の lifecycle が発生することがあります。
特に App Links のように
「再起動」や「復帰」が絡む機能では、
実際の挙動を確認しながら設計することが重要だと感じました。
Discussion