My web app works great in Chrome which handles configuration changes (such as screen rotation) excellent. Everything is perfectly preserved.
When loading my web app into a WebView
in my Android app then the web app loses state on screen orientation change. It does partially preserve the state, i.e. it preserves the data of the <input>
form elements, however all JavaScript variables and DOM manipulation gets lost.
I would like my WebView to behave the way Chrome does, i.e. fully preserving the state including any JavaScript variables. It should be noted that while Chrome and WebView derives from the same code base Chrome does not internally use WebView.
What happens on screen orientation change is that the Activity (and any eventual Fragments) gets destroyed then subsequently recreated. WebView
inherits from View
and overrides the methods onSaveInstanceState
and onRestoreInstanceState
for handling configuration changes hence it automatically saves and restores the contents of any HTML form elements as well as the back/forward navigation history state. However the state of the JavaScript variables and the DOM is not saved and restored.
There have been a few proposed solutions. All of them non-working, only preserving partial state or in other ways suboptimal.
WebView
inherits from View
which had the method setId
which can also be declared in the layout XML file using the android:id
attribute in the declaration of the <WebView>
element. This is necessary for the state to be saved and restored, however the state is only partially restored. This restores form input elements but not JavaScript variables and the state of the DOM.
and onRetainNonConfigurationInstance
are deprecated since API level 13.getLastNonConfigurationInstance
An Activity can have its screen orientation forced by setting the screenOrientation
attribute for the <Activity>
element in the AndroidManifest.xml
file or via the setRequestedOrientation
method. This is undesired as it breaks the expectation of screen rotation. This also only deals with the change of screen orientation and not other configuration changes.
Does not work. Calling the method setRetainInstance
on a fragment does retain the fragment (it does not get destroyed), hence all the instance variables of the fragment are preserved, however it does destroy the fragment's view hence the WebView
does gets destroyed.
The configChanges
attribute can be declared for an Activity
in the AndroidManifest.xml
file as android:configChanges="orientation|screenSize"
to handle configuration changes by preventing them. This works, it prevents the activity from getting destroyed hence the WebView
and its contents is fully preserved. However this has been discouraged and is said to be used only as a last resort solution as it may cause the app to break in subtle ways and get buggy. The method onConfigurationChanged
gets called when the configChanges
attribute is set.
I heard MutableContextWrapper
can be used, but I haven't evaluated this approach.
WebView
have the methods saveState
and restoreState
. Note according to the documentation the saveState
method no longer stores the display data for the WebView whatever that means. Either way these methods do not seem to fully preserve the state of the WebView.
The WebViewFragment
is just a convenience fragment that wraps WebView
for you so can easily get going with less boilerplate code, much like the ListFragment
. It does not do any additional state preserving to fully preserve the state.
This class was deprecated in API level 28.
Is there any real solution to the problem of WebView
getting destroyed and losing its state upon configuration changes? (such as screen rotation)
A solution that fully preserves all the state including JavaScript variables and DOM manipulation. A solution that is clean and not built on hacks or deprecated methods.
After researching and trying out different approaches I have discovered what I have come to believe is the optimal solution.
It uses setRetainInstance
to retain the fragment instance along with addView
and removeView
in the onCreateView
and onDestroyView
methods to prevent the WebView
from getting destroyed.
MainActivity.java
public class MainActivity extends Activity {
private static final String TAG_FRAGMENT = "webView";
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
WebViewFragment fragment = (WebViewFragment) getFragmentManager().findFragmentByTag(TAG_FRAGMENT);
if (fragment == null) {
fragment = new WebViewFragment();
}
getFragmentManager().beginTransaction().replace(android.R.id.content, fragment, TAG_FRAGMENT).commit();
}
}
WebViewFragment.java
public class WebViewFragment extends Fragment {
private WebView mWebView;
public WebViewFragment() {
setRetainInstance(true);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_webview, container, false);
LinearLayout layout = (LinearLayout)v.findViewById(R.id.linearLayout);
if (mWebView == null) {
mWebView = new WebView(getActivity());
setupWebView();
}
layout.removeAllViews();
layout.addView(mWebView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
return v;
}
@Override
public void onDestroyView() {
if (getRetainInstance() && mWebView.getParent() instanceof ViewGroup) {
((ViewGroup) mWebView.getParent()).removeView(mWebView);
}
super.onDestroyView();
}
private void setupWebView() {
mWebView.loadUrl("https:///www.example.com/");
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With