You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@cordova.apache.org by "Shingo Toda (JIRA)" <ji...@apache.org> on 2014/06/13 15:40:01 UTC

[jira] [Created] (CB-6936) app crashes if webview is destroyed while dialog box open

Shingo Toda created CB-6936:
-------------------------------

             Summary: app crashes if webview is destroyed while dialog box open
                 Key: CB-6936
                 URL: https://issues.apache.org/jira/browse/CB-6936
             Project: Apache Cordova
          Issue Type: Bug
          Components: Android, Plugin Dialogs
    Affects Versions: 3.4.0
         Environment: Android 4.x on physical device
Or any Android environment
            Reporter: Shingo Toda


We have an Android application which implements an embedded WebView "container" in which it executes customer Cordova apps.

Under certain conditions our container needs to terminate the customer app, and during this termination it calls {{CordovaWebView.destroy()}} to disable CordovaWebView.

But application may entirely crash during this termination in some scenarios. For example:

# call {{navigator.notification.alert}}, {{confirm}} or {{prompt}} and leave popup dialog open
# this termination is done for some reason while dialog box is till open
# all views on CordovaWebView are released but dialog box still remains
# attempt to close dialog by pressing button in the dialog
# application crashes with Unknown exception.

{code}
06-13 21:45:19.765: E/FSP_INTS-MAPS_AG(11176): 81508: Unknown exception occurred.
06-13 21:45:19.765: E/FSP_INTS-MAPS_AG(11176): java.lang.NullPointerException
06-13 21:45:19.765: E/FSP_INTS-MAPS_AG(11176): 	at android.webkit.WebView.setNetworkAvailable(WebView.java:2639)
06-13 21:45:19.765: E/FSP_INTS-MAPS_AG(11176): 	at org.apache.cordova.NativeToJsMessageQueue$OnlineEventsBridgeMode$1.run(NativeToJsMessageQueue.java:305)
06-13 21:45:19.765: E/FSP_INTS-MAPS_AG(11176): 	at android.app.Activity.runOnUiThread(Activity.java:4175)
06-13 21:45:19.765: E/FSP_INTS-MAPS_AG(11176): 	at org.apache.cordova.NativeToJsMessageQueue$OnlineEventsBridgeMode.onNativeToJsMessageAvailable(NativeToJsMessageQueue.java:313)
06-13 21:45:19.765: E/FSP_INTS-MAPS_AG(11176): 	at org.apache.cordova.NativeToJsMessageQueue.enqueueMessage(NativeToJsMessageQueue.java:253)
06-13 21:45:19.765: E/FSP_INTS-MAPS_AG(11176): 	at org.apache.cordova.NativeToJsMessageQueue.addPluginResult(NativeToJsMessageQueue.java:246)
06-13 21:45:19.765: E/FSP_INTS-MAPS_AG(11176): 	at org.apache.cordova.CordovaWebView.sendPluginResult(CordovaWebView.java:572)
06-13 21:45:19.765: E/FSP_INTS-MAPS_AG(11176): 	at org.apache.cordova.CallbackContext.sendPluginResult(CallbackContext.java:64)
06-13 21:45:19.765: E/FSP_INTS-MAPS_AG(11176): 	at org.apache.cordova.dialogs.Notification$1$1.onClick(Notification.java:150)
06-13 21:45:19.765: E/FSP_INTS-MAPS_AG(11176): 	at com.android.internal.app.AlertController$ButtonHandler.handleMessage(AlertController.java:167)
{code}

h2. Problem

Obviously CordovaWebView is already not available but dialog still exists. If a user press button in dialog, {{onClick()}} gets called and consequently {{CordovaWebView.setNetworkAvailable()}} gets called to send message from queue to JavaScript even though WebView is already destroyed. As a result, entire application crashes.


h3. Workaround

We made a method to check if WebView is destroyed or not.

{code}
    private boolean isWebViewDestroyed() {
    	final String url = webView.getUrl();
    	if (url == null ||
    		url.equals("about:blank")) {
    		return true;
    	} else {
        	return false;
    	}
    }
{code}

And check this before {{callbackContext.sendPluginResult()}} is called.

{code}
    public synchronized void alert(final String message, final String title, final String buttonLabel, final CallbackContext callbackContext) {

        final CordovaInterface cordova = this.cordova;

        Runnable runnable = new Runnable() {
            public void run() {

                AlertDialog.Builder dlg = new AlertDialog.Builder(cordova.getActivity());
                dlg.setMessage(message);
                dlg.setTitle(title);
                dlg.setCancelable(true);
                dlg.setPositiveButton(buttonLabel,
                        new AlertDialog.OnClickListener() {
                            public void onClick(DialogInterface dialog, int which) {
                                dialog.dismiss();
                                if (!isWebViewDestroyed()) {
                                    callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 0));
                                }
                            }
                        });
                dlg.setOnCancelListener(new AlertDialog.OnCancelListener() {
                    public void onCancel(DialogInterface dialog)
                    {
                        dialog.dismiss();
                        if (!isWebViewDestroyed()) {
                            callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 0));
                        }
                    }
                });

                dlg.create();
                dlg.show();
            };
        };
        this.cordova.getActivity().runOnUiThread(runnable);
    }
{code}

Similar changes are also applied to {{confirm()}} and {{prompt()}}. This workaround works fine on my application.

h2. Question

I want to know if this workaround is correct.

* Do you know how to check if WebView is destroyed or not? I end up choosing {{getURL()}} as a result of experimental test. I appreciate any official or popular way.
* {{isWebViewDestroyed()}} could be before {{webView.sendPluginResult(pluginResult, callbackId)}} in {{CallbackContext.sendPluginResult()}}? I think this way would prevent any APIs from this problem. But I'm afraid if this idea may impacts on other plugin API calls.




--
This message was sent by Atlassian JIRA
(v6.2#6252)