I know this has been asked too many times and that SO is full of similar questions. I went through most of them and I've been researching this issue for a couple of days and I have yet to find the definitive solution.
I could easily avoid all this trouble adding configChanges="orientation|screenSize"
to the activity containing the WebView; I've tested this and it worked as intended (the WebView didn't reload). But I really wanted to avoid this solution.
Here's my current WebView implementation:
public class BrowserFragment extends Fragment {
private WebView mWebView;
public BrowserFragment() {
// Required empty public constructor
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if (mWebView == null) {
mWebView = new WebView(getActivity());
}
return mWebView;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mWebView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return super.shouldOverrideUrlLoading(view, url);
}
});
mWebView.getSettings().setAllowFileAccess(true);
mWebView.getSettings().setBuiltInZoomControls(false);
mWebView.getSettings().setDomStorageEnabled(true);
mWebView.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.getSettings().setSupportZoom(false);
if (savedInstanceState == null) {
mWebView.loadUrl("http://developer.android.com/");
} else {
mWebView.restoreState(savedInstanceState);
}
}
@Override
public void onResume() {
mWebView.onResume();
super.onResume();
}
@Override
public void onPause() {
super.onPause();
mWebView.onPause();
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mWebView.saveState(outState);
}
@Override
public void onDestroyView() {
if (getRetainInstance() && mWebView.getParent() instanceof ViewGroup) {
((ViewGroup) mWebView.getParent()).removeView(mWebView);
}
super.onDestroyView();
}
}
As for the MainActivity
, there's nothing there besides setting the content view to the following layout file:
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="@+id/fragment_browser"
android:name="com.example.app.BrowserFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</merge>
Notice above that I'm retaining the fragment instance and I'm also saving the state of the WebView so I can late restore that same state. This is what the documentation has to say about the restoreState
method:
Restores the state of this WebView from the given Bundle. This method is intended for use in onRestoreInstanceState(Bundle) and should be called to restore the state of this WebView. If it is called after this WebView has had a chance to build state (load pages, create a back/forward list, etc.) there may be undesirable side-effects. Please note that this method no longer restores the display data for this WebView.
Meaning, the display data will not be restored but things like the scroll position will (I also tested this and it's working nicely). That's why I am creating a new instance of the WebView, only if it's null
during onCreateView
and removing it from the parent view during onDestroyView
. This was taken from this answer: https://stackoverflow.com/a/32801278/40480
This mechanism will make the rotate process a little bit more smooth (without it, a full blank page was shown) but won't prevent the WebView from still reloading the page.
Any ideas how can I possibly solve this problem?