9

I've searched many threads and still cannot find the answer to my question. I'm working on an Android app which uses WebView.

I use onSaveInstanceState() and onRestoreInstanceState() to save the WebView state like this:

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
    webView.saveState(savedInstanceState);
}

and

@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
    webView.restoreState(savedInstanceState);
}

I also have this in my onCreate():

public void onCreate(Bundle savedInstanceState) {
    ... other code ...
    if(savedInstanceState != null){
        webView.saveState(savedInstanceState);
    }else{
        webView.loadUrl("http://mypage");
    }
}

The problem: It seems that restoring WebView does not restore the Javascript variables/environment/workspace at all. When app is killed in the background and then restored, the whole Javascript is gone with all the objects/variables. The javascript code is name-spaced, i.e. window.utilitiesPack, window.eventHandlers, window.automation, etc and they are undefined. Also JQuery and other javascript plugins are used: all of them seem to be undefined after restore state. Basically the whole Javascript is not restored.

Can someone confirm or disprove that this is how it really is (Javascript not saved)? If the whole Javascript workspace is not saved, then what exactly does WebView.saveState() save? Is there some easy/elegant way to use existing API to preserve the Javascript objects?

//======================================================

Update1: So the problem remains. The most significant difficulty is this:

I am launching a Camera Intent for a result. When picture is taken, app gets back to WebView activity and is supposed to use Javascript to update HTML5 LocalStorage with some data variables.

The main Activity with WebView gets killed while Camera Activity is displayed, so when we come back to WebView, there is no Javascript anymore and no functions I can call from Android code. This happens every single time on Galaxy S3. It still happens on other phones, but not every time on picture taking.

Don't know what to do here. Somehow I must make the main Activity with WebView to retain the state while the picture is being taken with the Camera Intent. Does anyone have an idea how this can be achieved?

Michael Alan Huff
  • 3,462
  • 3
  • 28
  • 44
user2113581
  • 277
  • 1
  • 4
  • 11
  • 1
    "Can someone confirm or disprove that this is how it really is" - didn't you just confirm this by trying it and seeing that's indeed what happens? – millimoose Feb 27 '13 at 02:31
  • (The documentation seems to imply that what's saved is a `WebBackForwardList`.) – millimoose Feb 27 '13 at 02:33
  • 1
    "didn't you just confirm this by trying it and seeing that's indeed what happens?" I just can't believe that the entire Javascript is not saved. I'm hoping I'm not understanding something because if that's the case, this is such a limitation for apps that use webpages with complex javascript function. – user2113581 Feb 27 '13 at 02:37
  • Did I understand it right: `WebBackForwardList` actually saves the Javascript stuff? – user2113581 Feb 27 '13 at 02:38
  • Ok, I see what you mean. Thanks for your help though. – user2113581 Feb 27 '13 at 02:48
  • 1
    Hey @user2113581, this is a problem that more people are having. Could you write up a solution to your own question instead of the comment you left below? – Michael Alan Huff Mar 26 '14 at 19:11
  • @user2113581: could you please provide your code solution? I have the same problem with you, I could not make WebView save the state of current page as well as the javascript space. Thank you – Richard Le Jul 21 '14 at 08:54

2 Answers2

8

As user2113581 commented, moving the WebView into a context of the application rather than the Activity is a potential solution. This still has all of the pitfalls that user2113581 mentioned, including <select> not working (because it creates a window, but an application context does not have a window token).

I have extended my Application to manage my webview...

MyApplication.java:

public class MyApplication extends Application {
  private WebView mWebView;
  private boolean mWebViewInitialized;
  // ...

  @Override public void onCreate() {
    super.onCreate();
    mWebView = new WebView(this);
    mWebViewInitialized = false; // we do some initialization once in our activity.
  }

  public WebView getWebView() { return mWebView; }
  public boolean isWebViewInitialized() { return mWebViewInitialized; }
  public void setWebViewInitialized(boolean initialized) {
    mWebViewInitialized = initialized;
  }
}

In our activity:

@Override protected void onCreate(Bundle savedInstanceState) {
  // ...
  MyApplication app = (MyApplication) getApplication();
  mWebView = app.getWebView();
  if (!app.isWebViewInitialized()) { 
    /* first time initialization */ 
    app.setWebViewInitialized(true);
  }
}

Finally, in your Activity, you will want to add the mWebView to a container view (FrameLayout, or similar) during a lifecycle event that makes sense. You'll need to remove it from the container when the activity is being paused or stopped. I've used onResume and onPause with good results.

Roel
  • 3,089
  • 2
  • 30
  • 34
Ryan Schultz
  • 109
  • 2
  • 4
  • Clever! Have you tried similar strategy for iOS too? – eugene Apr 10 '15 at 13:42
  • 3
    This is only a partial solution since Android OS has the permission to kill your application process entirely while in another application (e.g. camera app) so saving webview state on the application instance will not solve this issue. This can occur when system resources are low for example. – Steven Mark Ford Mar 01 '16 at 13:23
  • A problem I have found with this approach is that the Webview now has an ApplicationContext rather than an ActivityContext. This works fine until you try do "confirm(...)" call from JavaScript, then the application will crash. It seems that when the Webview has to popup a dialog it is trying to cast the context to ActivityContext and that fails with this implementation. – BruceHill Jul 05 '16 at 08:05
  • For this to work, you also need to add `android:name="fully.qualified.package.name.MyApplication"` to the `` tag in AndroidManifest.xml. See [this answer](https://stackoverflow.com/a/12834521/4284627) for an example. – Donald Duck Aug 14 '21 at 16:44
3

Very few information found, only this line:

Please note that this method no longer stores the display data for this WebView. The previous behavior could potentially leak files if restoreState(Bundle) was never called.

and this line:

Returns the same copy of the back/forward list used to save the state.

in the javadoc for WebView.saveState(Bundle).

MH.
  • 45,303
  • 10
  • 103
  • 116
diyism
  • 12,477
  • 5
  • 46
  • 46
  • 1
    Thank you for your reply. It's been a while since I've ran into this problem and so I forgot about my question here. I'm marking your answer as correct since it is. For those who runs into the same problem: the way I solved it is by defining webView in the context of App instead of in the context of Activity. Now everything works as expected. The only problem with this approach is you'll have to override onJsAlert() and onJsConfirm() to have alert()/confirm() working. Also the – user2113581 Jul 10 '13 at 21:20
  • 1
    This is only a partial solution since Android OS has the permission to kill your application process entirely while in another application (e.g. camera app) so saving webview state on the application instance will not solve this issue. This can occur when system resources are low for example. – Steven Mark Ford Mar 01 '16 at 13:23