1. 悩んでいたこと
Activity/Fragmentがバックグラウンドになった後(ホームボタン押下とか)、復帰したときにWebViewの状態を復元させたい(=最後に表示していたページを表示させたい)。
特に、バックグラウンド中にActivityが破棄されたときが問題でした。
2. 構成
FragmentがWebViewを持っています。
大人の事情があって、WebViewFragmentを使っていません。
結論だけ見たい人は、5.へ飛んで下さい。
3. アプローチ
基本的なことで、Fragmentがバックグラウンドから復帰する場合は、
onSaveStateInstance → onPause → onStop→(バックグラウンド) → onStart
という流れで呼ばれます。
バックグラウンドで破棄された場合(Activityを保持しないにチェックが入ってたり、単純にOSメモリが貧弱な場合)は、
onSaveStateInstance → onPause → onStop→(バックグラウンド) → onCreateView → onStart
という流れになります。
ということで、ちゃんとお作法に従って、onSaveStateInstanceで必要な情報を保存してやるようにすればいいんじゃん?と思って、実装しました。
@Override
public void onSaveStateInstance(Bundle outState){
// 最後に表示していたページを保存
outState.putString("last_url", mWebView.getUrl());
}
で、復元時の処理を書く。
@override
public void onCreateView(Bundle savedInstantState){
...
mSavedUrl = null;
if(savedInstantState!=null){
mSavedUrl = savedInstantState.getString("last_url");
}else{
loadUrl("default_url");
}
}
@Override
public void onViewStateRestored(Bundle outState){
mSavedUrl = outState.getString("last_url");
}
@Override
public void onStart(){
loadUrl(mSavedUrl);
super.onStart();
}
@Override
public void onStop(){
mSavedUrl = mWebView.getUrl(); // 最後に表示していたページを保存
super.onStop();
}
onViewStateRestoredが呼ばれるのを見たことが無いけど、一応書いておきます。
ちなみに、loadUrlはこんな感じ。
public void loadUrl(String url){
if(url==null)
return;
String now = mWebView.getUrl();
if(url.equals(now)) return; // (**)
mWebView.loadUrl(url);
}
なお、再読み込みを防止するため(**)の処理を入れていますが、強制的にリロードさせたい場合もあるでしょうから、その辺は臨機応変にやりましょう(パラメータにフラグを追加するとか)。
これはランダムに変わるページにアクセスしていたとき、再読込されてしまうことで「同じページの状態に戻れない」ことを回避するために入れました。
で、これで、とりあえず見ていたページには戻るようになった。
目的を達成したかに思われました。
4. 問題点
それは戻るボタンを押したときに発覚しました。
WebViewに履歴がある場合(canGoBack()==true)は、ブラウザバックするように実装していたのですが、Activityがバックグラウンドで破棄された場合に、履歴がなくなってしまうのです。
まあ当然っちゃ当然です。その場合はWebView自体新しいインスタンスになっているのですから。
まあこれは、仕様として許容してもらうかなーなんて甘いこと考えてましたが、何となく納得いかない。
で、Android DevelopersサイトでWebViewのページを眺めてました。
5. 結論
WebView#saveStateとWebView#restoreStateというメソッドを発見して、万事解決しました。
関連箇所のコードです。
@override
public void onCreateView(Bundle savedInstantState){
...
if(savedInstantState!=null){
mWebView.restoreState(savedInstantState);
}else{
loadUrl("default_url");
}
}
@Override
public void onViewStateRestored(Bundle outState){
mWebView.restoreState(savedInstantState);
}
@Override
public void onSaveStateInstance(Bundle outState){
mWebView.saveState(outState);
}
mSavedUrlは不要で、onStartとonStopのオーバーライドも不要になりました。
6. その他
WebViewFragmentのソースコードを見て、以下も追加しました。
これをしないと動画や音声がバックグラウンドで止まらないそうです。
@Override
public void onResume() {
super.onResume();
mWebView.onResume();
}
@Override
public void onPause() {
super.onPause();
mWebView.onPause();
}
@Override
public void onDestroy() {
if (mWebView != null) {
mWebView.destroy();
mWebView = null;
}
super.onDestroy();
}
え、、、Viewのdestroyって自動で呼ばれないんだ・・・
それともWebViewだけ??
と恐ろしくなりました・・・