Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WebViewClient.onPageStarted( ) called twice when specifying non-existent URL via WebView.loadURL()

Here is my code

public class Main extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);


    WebView webView = (WebView)findViewById(R.id.webView);

    // Assign webclient.
    webView.setWebViewClient(new WebViewClient( ) {
        @Override
        public void onPageStarted(WebView view, String url, Bitmap favicon) {
            Log.d("TAG", url);
        }

        @Override
        public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
            Log.d("TAG", "failed: " + failingUrl + ", error code: " + errorCode + " [" + description + "]");
        }
    });


    webView.loadUrl("http://m.vooglemoogle.com" );
}



}

Results in the following log:

03-29 13:40:27.005: DEBUG/TAG(10948): http://m.vooglemoogle.com/
03-29 13:40:27.599: DEBUG/TAG(10948): failed: http://m.vooglemoogle.com/, error code: -2[The URL could not be found.]
03-29 13:40:27.607: DEBUG/TAG(10948): http://m.vooglemoogle.com/

Note another call to onPageStarted( ) ... Does anyone know the reason behind this? cheers!

like image 677
Dr. Benedict Porkins Avatar asked Mar 29 '11 17:03

Dr. Benedict Porkins


2 Answers

I encountered the same problem while testing my app on an AVD with API 7 (not sure if this is relevant but in any case).

I noticed that the exact sequence of callbacks is the following:

onPageStarted()     // url = non-existing url
onLoadResource()    // url = non-existing url
onReceivedError()   // url = non-existing url

onPageStarted()     // url = non-existing url
onLoadResource()    // url = file://android_assed/webkit/android-weberror.png
onPageFinished()    // url = non-existing url

So I guess the loading of the Android "Web page not available" page is triggering the second onPageStarted call.

like image 71
THelper Avatar answered Nov 15 '22 17:11

THelper


I struggled with this as well, but I think I've worked around it. Basically I set a flag on error to keep the client from processing any more callbacks. The flag gets reset when I call a method in the activity to try the load again. Here is some sample code from a gist I created https://gist.github.com/museofwater/6373048

public class AsyncMultiplayerSetupActivity extends Activity {

    private static final String TAG = AsyncMultiplayerSetupActivity.class.getName();
    public static final String UTF_8 = "UTF-8";

    private WebView wvSignin;
    private String url = "http://localhost:9000/signin";
    private ProgressDialog progressLoadUrl;
    private SigninWebViewClient webViewClient;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Intent intent = getIntent();
        registerUser(url);
    }

    private void registerUser(String url) {
        webViewClient = new SigninWebViewClient(getResources().getInteger(R.integer.timeout));
        setContentView(R.layout.setup);
        wvSignin = (WebView)findViewById(R.id.wvSignin);
        wvSignin.getSettings().setJavaScriptEnabled(true);
        wvSignin.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
        wvSignin.setWebViewClient(webViewClient);
        loadUrl(url);
    }

    private void loadUrl(String url) {
        if (!NetworkUtil.checkNetwork(this)) {
            setSigninFailureResult();
        }
        // Show progress
        progressLoadUrl =
                ProgressDialog.show(this, getString(R.string.CONNECTING_TITLE),
                        getString(R.string.CONNECTING_MSG));
        webViewClient.prepareToLoadUrl();
        wvSignin.loadUrl(url);
    }

    private void setSigninFailureResult() {
        setResult(getResources().getInteger(R.integer.RESPONSE_FAILED_CODE));
        finish();
    }

    private void setSigninResult() {    
        setResult(getResources().getInteger(R.integer.RESPONSE_OK_CODE));
    }

    private class SigninWebViewClient extends WebViewClient {
        /**
         * Timeout for page load in seconds
         */
        private int timeout;
        private String urlLoading;

        boolean pageLoaded = false;

        // Flag to instruct the client to ignore callbacks after an error
        boolean hasError = false;

        private Handler timeoutHandler;
        private AlertDialog alertDialog;

        private SigninWebViewClient(int timeout) {
            this.timeout = timeout;
            timeoutHandler = new Handler();
        }

        // Called by activity before requesting load of a url
        private void prepareToLoadUrl() {
           this.hasError = false;
           this.pageLoaded = true;
           this.urlLoading = null;
        }

        @Override
        public void onPageStarted(WebView view, String url, Bitmap favicon) {
            if (hasError) {
                return;
            }
            urlLoading = url;
            // timeout has expired if this flag is still set when the message is handled
            pageLoaded = false;
            Runnable run = new Runnable() {
                public void run() {
                    // Do nothing if we already have an error
                    if (hasError) {
                        return;
                    }

                    // Dismiss any current alerts and progress
                    dismissProgress();
                    dismissErrorAlert();
                    if (!pageLoaded) {
                        showTimeoutAlert();
                    }
                }
            };
            timeoutHandler.postDelayed(run, this.timeout*1000);
        }

        @Override
        public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
            // Ignore future callbacks because the page load has failed
            hasError = true;
            dismissProgress();
            showServerErrorAlert();
        }

        @Override
        public void onPageFinished(WebView view, String url) {
            if (hasError) {
                return;
            }
            pageLoaded = true;
            dismissProgress();
            dismissErrorAlert();
            urlLoading = null;

            // Do whatever processing you need to on page load here
         }

        private void showTimeoutAlert() {
            showErrorAlert(R.string.TIMEOUT_TITLE, R.string.TIMEOUT_MSG);
        }

        private void showServerErrorAlert() {
            showErrorAlert(R.string.SERVER_ERROR_TITLE,R.string.SERVER_ERROR_MSG);
        }

        private void showErrorAlert(int titleResource, int messageResource) {
            AlertDialog.Builder builder = new AlertDialog.Builder(AsyncMultiplayerSetupActivity.this);
            // Add the buttons
            builder.setTitle(titleResource)
                    .setMessage(messageResource)
                    .setPositiveButton(R.string.RETRY, new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int id) {
                            // Try to load url again
                            loadUrl(urlLoading);
                            dialog.dismiss();
                        }
                    });
            builder.setNegativeButton(R.string.CANCEL, new DialogInterface.OnClickListener() {
                 public void onClick(DialogInterface dialog, int id) {
                     // User cancelled the dialog
                     setSigninFailureResult();
                     dialog.cancel();
                }
            });

            // Create the AlertDialog
            alertDialog = builder.create();
            alertDialog.show();
        }

        private void dismissProgress() {
            if (progressLoadUrl != null && progressLoadUrl.isShowing()) {
                progressLoadUrl.dismiss();
            }
        }

        private void dismissErrorAlert() {
            if (alertDialog != null && alertDialog.isShowing()) {
                alertDialog.dismiss();
            }
        }
    }
}
like image 32
museofwater Avatar answered Nov 15 '22 18:11

museofwater