How best to implement error handling and timeout on WebView when loading a page?

JohnHardmanJohnHardman GBUniversity mod

I make use of WebView within my app. It seems to be a bit short on error handling and timeout handling, which occasionally results in, well... nothing - just a blank view.

How can I:
(1) get a status from the WebView telling me whether it is still loading the DOM, has loaded the DOM and rendered the view, has got an error (and what the error is) etc?
(2) get notified when the status in (1) changes
(3) specify a timeout on a WebView? (if I can identify whether it has been populated using (1) I can probably workaround this using a timer)

And, just in case I need it in future:
(4) access the DOM?

Many thanks,

John H.

Best Answer

Answers

  • jefnazariojefnazario USMember ✭✭

    Hi @JohnHardman,

    Did you try to use a try-catch on your code snippet which loads a WebView?
    When the WebView has timed out, the framework throws an exception.

  • JohnHardmanJohnHardman GBUniversity mod

    Hi @jefnazario

    I don't have a try/catch around the WebView at the moment (I should do, oops...), but no exception is being thrown on the occasions when the WebView never populates with the expected data. How do you specify the timeout period?

    Thanks,

    John H.

  • jefnazariojefnazario USMember ✭✭

    Hello @JohnHardman,
    I didn't need, yet, specify the timeout from webView request, so, I'm sorry, I can't help you on this.

    About the try-catch, when I used WebView the try-catch instruction worked fine to me. Check if the website which you're making the request is up or down, maybe the request was timed out only in the website page, and not on WebView request.

    Let me know if that solve your problem.
    Best regards.

  • JohnHardmanJohnHardman GBUniversity mod

    Hello @jefnazario

    The web-site that I have been using for testing is one that is very slow to start up if it hasn't been accessed recently. It can take a good 20 seconds or more. On subsequent accesses, it is almost instantaneous (as far as a user is concerned).

    I haven't monitored the network traffic yet to see exactly what is happening, but occasionally I end up with a WebView that is unpopulated, which I would not have expected. It may be that the slow web-site start-up messes with any timeout handling internal to the WebView.

    If the web-site has been accessed recently, the WebView works fine, which makes me think something is awry with the timeout (or other error) handling.

    If there is no explicit method of specifying timeouts etc, I guess I will have to sniff the network traffic. I'm going to be studying for a university exam for the next couple of weeks, so will check the traffic when I get back to app development after that.

    Thanks again,

    John H.

  • hvaughanhvaughan USMember ✭✭✭

    @JohnHardman
    If you are using IIS then it is most definitely one of the issues talked about here:
    https://serverfault.com/questions/590865/how-can-i-warm-up-my-asp-net-mvc-webapp-after-an-app-pool-recycle

    In regards to the WebView, if you make a custom renderer, you can override the LoadFailed() method to find out what kind of errors are happening and decide what to do about them. For instance, I check to see if an error comes in talking about "internet connection appears to be offline". If I find that, I send a notification through MessagingCenter to display an alert.

  • JohnHardmanJohnHardman GBUniversity mod

    @hvaughan - Many thanks for your response.

    Not IIS I'm afraid.

    Regarding LoadFailed(), could you share how you have done this please? I've had a look at custom WebView renderers for Android and iOS, but haven't spotted where the LoadFailed() override would go.

  • JohnHardmanJohnHardman GBUniversity mod

    @hvaughan - Apologies for the delay in replying, and many thanks for the iOS code. It works a treat - for the time being, I've added a few lines to create an HTML message in case of error that is then displayed in the WebView. I need to pretty up the HTML, but it's functional.

    Yes please, if you have an Android equivalent, could you post that as well please?

    Many thanks again,

    John H.

  • hvaughanhvaughan USMember ✭✭✭
    edited November 2015

    @JohnHardman

    No problem and sure thing.

    Depending on the version of Xamarin Android your solution is running, you may or may not need that second OnReceivedError method (see the summary comments for an explanation), so I will comment it out and will also comment out the [Obsolete] annotation (which may not even be necessary with the latest Xamarin Android version, but ReSharper suggested it). So if you build you project and it complains about a deprecated OnReceivedError method, then you can uncomment the method:

    public class FormsWebViewRenderer : WebViewRenderer {
    
        public FormsWebView FormsWebViewItem { get { return Element as FormsWebView; } }
    
        #region Event Handlers
    
        protected override void OnElementChanged(ElementChangedEventArgs<WebView> e) {
            base.OnElementChanged(e);
    
            if(e.OldElement == null) {
                Control.SetWebViewClient(new FormsWebViewClient(this, FormsWebViewItem));   //Only needed if you need a reference to the Forms (non custom renderer) WebView
                //Control.SetWebChromeClient(new WebChromeClient());    //Might not be needed for you
            }
        }
    
        #endregion
    }
    
    internal class FormsWebViewClient : WebViewClient {
    
        #region Properties
    
        private readonly FormsWebView         _formsWebView;
        private readonly FormsWebViewRenderer _formsRenderer;
    
        private          string                _previousUrl;
    
        #endregion
    
        #region Constructors
    
        /// <summary>
        /// Initializes an instance of <see cref="FormsWebViewClient"/>.
        /// </summary>
        /// <param name="formsRenderer">The FormsWebView Renderer.</param>
        /// <param name="formsWebView">The Xamarin WebView to configure from.</param>
        public FormsWebViewClient(FormsWebViewRenderer formsRenderer, FormsWebView formsWebView) {
    
            #region Properties
    
            _formsRenderer   = formsRenderer;
            _formsWebView    = formsWebView;
    
            #endregion
        }
    
        #endregion
    
        #region Event Handlers
    
        public override void OnPageStarted(Android.Webkit.WebView view, string url, Bitmap favicon) {
            base.OnPageStarted(view, url, favicon);
    
        }
    
        public override void OnReceivedSslError(Android.Webkit.WebView view, SslErrorHandler handler, SslError error) {
            base.OnReceivedSslError(view, handler, error);                    //Comment this out so it will not handle the error on it's own when dealing with self-signed certs mostly
    
        }
    
        /// <summary>
        /// Used for redirecting
        /// </summary>
        /// <param name="view">The Android WebView that set off the event.</param>
        /// <param name="url">The URL that is being visited.</param>
        /// <returns>bool, true means we dealt with the URL, false means that the system needs to deal with it.</returns>
        public override bool ShouldOverrideUrlLoading(Android.Webkit.WebView view, string url) {
    
            //If you want to redirect you would use a condition and then return true
            return false;
        }
    
        /// <summary>
        /// This method reports unrecoverable errors from the main page only. It has been deprecated for the event handler below which receives errors for the main page as well as iframe, images, etc.
        /// </summary>
        //[Obsolete("deprecated")]
        public override void OnReceivedError(Android.Webkit.WebView view, ClientError errorCode, string description, string failingUrl) {
            base.OnReceivedError(view, errorCode, description, failingUrl);
            Console.WriteLine("\nIn AppName.Droid.FormsWebViewRenderer - Error\nError Code: {0}\nDescription: {1}\nFailing URL: {2}\n", errorCode, description, failingUrl); //TODO: Do something more useful
    
            //view.LoadData(/* Add your custom error message here along with the 'description' parameter or whatever */, "text/html", "UTF-8");                                         //Load custom errors
        }
    
        //public override void OnReceivedError(Android.Webkit.WebView view, IWebResourceRequest request, WebResourceError error) {
            //base.OnReceivedError(view, request, error);
            //Console.WriteLine("\nIn AppName.Droid.FormsWebViewRenderer - Error\nError Code: {0}\nDescription: {1}\nFailing URL: {2}\n", error.ErrorCode.ToString(), error.DescriptionFormatted.ToString(), request.Url.ToString()); //TODO: Do something more useful
            //view.LoadData(/* Add your custom error message here */, "text/html", "UTF-8");                                            //Load custom errors
        //}
    
        public override void OnPageFinished(Android.Webkit.WebView view, string url) {
    
            //I use this to collect cookies and also detect if we hit a login page and, as a last resort, attempt to submit the login form using JavaScript
         }
    
        #endregion
    }
    

    There a bunch of Android WebView specific settings having to do with JavaScript, Zooming, cookies, back buttons, etc. that I left out but let me know if you would like to see those too.

  • arlvinarlvin Member

    Thanks for the solution. I was actually looking for the Android implementation and your solution worked perfectly. I must admit, i'm not very knowledgeable when it comes to custom renderers therefore resolving the missing 'using' statements was a bit of a challenge. And i was actually working on hope because first of all, i had to change 'FormsWebView' to 'WebView', something i wasn't sure about. Then there is the 'class FormsWebViewClient : WebViewClient' where the magic lives. I don't know what this class is, but your comments helped point me to the 'region' of interest (i.e. '#region Event Handlers') where i modified the code to handle errors. Could you explain what 'FormsWebViewClient' is.

Sign In or Register to comment.