この記事はand factory.inc Advent Calendar 2025 24日目の記事です。
昨日は @chitomo12 さんの SwiftDataの痒いところに手が届くTipsの話 でした ![]()
Android アプリで WebView を使っていると、画面回転対応まわりで少し悩むことがあります。
この記事では、WebView の画面回転対応を行った際に遭遇したクラッシュについて、restoreState() の仕様と実際の挙動をもとにまとめてみました。
同じように saveState() / restoreState() を使っている方の、
ちょっとした参考になればうれしいです。
バグの概要と再現条件
今回のクラッシュは、WebView の画面回転対応と画面遷移が重なった場合に発生していました。
再現手順は次のような流れです。
- WebView を表示する画面を開く
- WebView から別の画面へ遷移する
- 遷移先で端末を回転する
この操作によって、アプリがクラッシュするケースがありました。
Crashlytics を確認すると、WebView の状態復元処理に関連したクラッシュが発生しており、
画面回転時の状態保存対応を追加したあとに表面化した問題であることが分かりました。
「WebView → 別画面 → 回転 → 戻る」という操作は、
実際の利用シーンでも起こり得るため、
一部の環境でのみ再現する 気づきにくいクラッシュ になっていました。
なぜ restoreState() を使っていたのか
今回の実装では、画面回転時でも WebView の表示状態をできるだけ維持したいと考え、
WebView#saveState() / restoreState() を使っていました。
WebView を含む画面では、
ページの再読み込みを避けたい
スクロール位置を維持したい
といった希望があり、onSaveInstanceState() で WebView の状態を保存し、
再生成時に restoreState() で復元することで、
画面回転前と近い状態に戻すことを狙っていました。
基本的なケースでは問題なく動いていましたが、
画面遷移と画面回転が重なった場合に、
restoreState() の失敗ケースを十分に考慮できていなかったことが、
今回のクラッシュにつながっていました。
該当コード(修正前)
画面回転時の状態復元では、WebView#restoreState() をそのまま呼び出していました。
if (savedInstanceState != null) {
binding.webview.restoreState(savedInstanceState)
} else {
binding.webview.loadUrl(url)
}
この実装では、restoreState() の戻り値を特に確認していませんでした。
WebView#restoreState() は、復元に成功すると WebBackForwardList を返しますが、
何らかの理由で失敗した場合は null を返すことがあります。
修正前のコードでは、このケースを考慮していなかったため、
画面回転や画面遷移のタイミングによっては
クラッシュにつながっていました。
バグの修正
今回対応したポイントは、主に次の2点です。
-
restoreState()の戻り値を確認し、失敗時は初期 URL を読み込む -
onSaveInstanceState()で安全に状態保存を行う
restoreState() のフォールバック
val restored = binding.webview.restoreState(savedInstanceState)
if (restored == null) {
binding.webview.loadUrl(url)
}
状態復元に失敗した場合は、
通常の初期表示に戻すようにしました。
onSaveInstanceState() の修正
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
saveWebViewState(outState)
}
private fun saveWebViewState(outState: Bundle) {
if (::binding.isInitialized) {
binding.webview.saveState(outState)
}
}
Fragment のライフサイクルを考慮し、
binding が初期化されている場合のみ状態保存を行うようにしています。
バグ修正の背景と考察
WebView#restoreState() は、公式ドキュメント上でも
復元に失敗した場合は null を返すと記載されています。
https://developer.android.com/reference/android/webkit/WebView#restoreState(android.os.Bundle)
画面回転と画面遷移が重なると、Fragment の View が再生成され、
保存された状態と WebView の内部状態がうまく噛み合わなくなることがあります。
そのような状況では、restoreState() が失敗し、
null が返ることがあるようです。
今回の対応から、restoreState() を使う場合は
フォールバック処理を用意しておくと安心だと感じました。
まとめ
-
WebView#restoreState()は、失敗するとnullを返すことがあるようです - 画面回転対応では、Fragment のライフサイクルを意識することが大切です
- 状態復元がうまくいかなかった場合に備えて、
フォールバック処理を入れておくと安心です
WebView の画面回転対応を行う際の、
ひとつの事例として参考になれば幸いです。