C# Xamarin使用CustomWebViewClient形成Android Web视图在加载特定站点后停止工作

C# Xamarin使用CustomWebViewClient形成Android Web视图在加载特定站点后停止工作,c#,xamarin.forms,xamarin.android,signalr,android-webview,C#,Xamarin.forms,Xamarin.android,Signalr,Android Webview,为了使用我的WebView发送身份验证标头,我有自定义WebViewRenderer和WebViewClient 可以为各种URL创建、加载、关闭并重新创建带有此控件的Xamarin forms页面,而不会出现任何问题。但是,在访问特定站点后,WebView将不再加载任何URL,并且根本不会调用ShouldInterceptRequest,即使在使用此控件关闭并创建新页面后也是如此 例如: 打开MyWebViewPage至www.google.com。成功 关闭MyWebViewPage 打开M

为了使用我的WebView发送身份验证标头,我有自定义WebViewRenderer和WebViewClient

可以为各种URL创建、加载、关闭并重新创建带有此控件的Xamarin forms页面,而不会出现任何问题。但是,在访问特定站点后,WebView将不再加载任何URL,并且根本不会调用ShouldInterceptRequest,即使在使用此控件关闭并创建新页面后也是如此

例如:

  • 打开MyWebViewPage至www.google.com。成功
  • 关闭MyWebViewPage
  • 打开MyWebViewPage至www.bing.com。成功
  • 关闭MyWebViewPage
  • 打开MyWebViewPage至mywebapp.local(问题页面)成功
  • 关闭MyWebViewPage
  • 打开MyWebViewPage至www.google.com。失败,页面不呈现
  • 如果没有CustomWebViewClient,则不会出现此问题(但是我需要将此用于标题)

    我正在寻找直接修复此问题的方法,或者完全重置此控件/页面的方法。 不适合对网站进行修复,因为这可能并不总是可能的

    调试上次成功满载的输出(到问题站点)

    调试后续故障加载的输出(到相同或其他站点),空白页:

    12-04 16:45:36.910 I/art     (23188): Starting a blocking GC Explicit
    12-04 16:45:36.941 I/art     (23188): Explicit concurrent mark sweep GC freed 5227(440KB) AllocSpace objects, 24(3MB) LOS objects, 62% free, 4MB/12MB, paused 302us total 31.346ms
    12-04 16:45:36.943 D/Mono    (23188): GC_TAR_BRIDGE bridges 362 objects 8823 opaque 2742 colors 362 colors-bridged 362 colors-visible 362 xref 0 cache-hit 0 cache-semihit 0 cache-miss 0 setup 0.11ms tarjan 9.11ms scc-setup 0.23ms gather-xref 0.01ms xref-setup 0.02ms cleanup 0.28ms
    12-04 16:45:36.943 D/Mono    (23188): GC_BRIDGE: Complete, was running for 35.88ms
    12-04 16:45:36.943 D/Mono    (23188): GC_MINOR: (Nursery full) time 18.20ms, stw 20.86ms promoted 187K major size: 4400K in use: 3752K los size: 13432K in use: 11411K
    12-04 16:45:36.973 W/cr_AwContents(23188): onDetachedFromWindow called when already detached. Ignoring
    12-04 16:45:36.979 I/cr_Ime  (23188): ImeThread is not enabled.
    
    using Xamarin.Forms;
    using MyApp.Droid;
    
    [assembly: ExportRenderer(typeof(WebView), typeof(CustomWebViewRenderer))]
    namespace MyApp.Droid
    {        
        using Android.OS;
        using global::Xamarin.Forms.Platform.Android;
        using Android.Content;
        using System;
    
        public class CustomWebViewRenderer : WebViewRenderer
        {        
            public CustomWebViewRenderer(Context context) : base(context)
            {
            }
    
            public IWebViewController GetElementController()
            {
                return Element;
            }
    
            protected override void OnElementChanged(ElementChangedEventArgs<global::Xamarin.Forms.WebView> e)
            {
                try
                {
                    base.OnElementChanged(e);
    
                    if (Control != null && e.NewElement != null)
                    {
                        SetupControlSettings();
                    }
                }
                catch (Exception ex)
                {
                    Logging.LogException(ex);
                }
            }           
    
            private void SetupControlSettings()
            {
                try
                {   
                    if (Build.VERSION.SdkInt >= BuildVersionCodes.JellyBeanMr1)
                    {
                        Control.Settings.MediaPlaybackRequiresUserGesture = false;
                        Control.Settings.JavaScriptEnabled = true;
                        Control.Settings.AllowContentAccess = true;
                        Control.Settings.DomStorageEnabled = true;
                    }
    
                    if ((int)Build.VERSION.SdkInt >= 19)
                    {
                        Control.SetLayerType(Android.Views.LayerType.Hardware, null);
                    }
                    else
                    {
                        Control.SetLayerType(Android.Views.LayerType.Software, null);
                    }
    
                    Control.Settings.SetAppCacheMaxSize(10 * 1024 * 1024);
                    Control.Settings.AllowFileAccess = true;
                    Control.Settings.AllowUniversalAccessFromFileURLs = true;
                    Control.Settings.SetAppCacheEnabled(true);
                    Control.Settings.CacheMode = Android.Webkit.CacheModes.NoCache;    
    
                    var webViewClient = new CustomWebViewClient(this, MyApp.GlobalVariables.AuthToken);               
    
                    Control.SetWebViewClient(webViewClient);
    
                }
                catch (Exception ex)
                {
                    Logging.LogException(ex);
                }
            }   
        }
    }
    
    using Android.Webkit;
    using System;
    using System.Net.Http;
    using Xamarin.Forms;
    
    namespace MyApp.Droid
    {
        public class CustomWebViewClient : WebViewClient
        {
            private readonly string authToken;
            private readonly CustomWebViewRenderer renderer;
            public CustomWebViewClient(CustomWebViewRenderer renderer, string authToken)
            {
                this.renderer = renderer;
                this.authToken = authToken;
            }
    
    
            public override bool ShouldOverrideUrlLoading(Android.Webkit.WebView view, IWebResourceRequest request)
            {
                WebNavigatingEventArgs args = new WebNavigatingEventArgs(WebNavigationEvent.NewPage, new UrlWebViewSource { Url = request.Url.ToString() }, request.Url.ToString());
                renderer.GetElementController().SendNavigating(args);
    
                if (args.Cancel)
                {
                    view.StopLoading();
                }
    
                return base.ShouldOverrideUrlLoading(view, request);
            }
    
    
            public override WebResourceResponse ShouldInterceptRequest(Android.Webkit.WebView view, IWebResourceRequest request)
            {
                try
                {
                    if (!string.IsNullOrWhiteSpace(authToken))
                    {
                        string authorizationValue = "Bearer " + authToken;    
                        using (HttpClient client = new HttpClient())
                        {    
                            request.RequestHeaders.Add("Authorization", authorizationValue);    
                            foreach(var h in request.RequestHeaders)
                            {
                                if (client.DefaultRequestHeaders.Contains(h.Key))
                                {
                                    client.DefaultRequestHeaders.Remove(h.Key);
                                }  
                                client.DefaultRequestHeaders.Add(h.Key,h.Value);                            
                            }    
                            HttpResponseMessage response = client.GetAsync(request.Url.ToString()).Result;
    
                            string encoding = response.Content.Headers.ContentEncoding.GetEnumerator().Current;
                            string contentType = response.Content.Headers.ContentType.ToString();
                            System.IO.Stream stream = response.Content.ReadAsStreamAsync().Result;
    
                            if (string.IsNullOrWhiteSpace(encoding) && contentType.Contains(";"))
                            {
                                string[] parts = contentType.Split(';');
                                contentType = parts[0].Trim();
                                encoding = parts[1].Trim();
                            }
    
                            WebResourceResponse result = new WebResourceResponse(contentType, encoding, stream);    
                            return result;
                        }
                    }
                }
                catch (Exception ex)
                {
                    Logging.LogException(ex);
                }
    
                return base.ShouldInterceptRequest(view, request);
            }
    
    
            public override void OnPageFinished(Android.Webkit.WebView view, string url)
            {
                try
                {
                    base.OnPageFinished(view, url);
    
                    UrlWebViewSource source = new UrlWebViewSource { Url = url };
                    WebNavigatedEventArgs args = new WebNavigatedEventArgs(WebNavigationEvent.NewPage, source, url, WebNavigationResult.Success);
                    renderer?.GetElementController()?.SendNavigated(args);
                }
                catch (Exception ex)
                {
                    Logging.LogException(ex);
                }
            } 
        }
    }
    
    WebViewRenderer:

    12-04 16:45:36.910 I/art     (23188): Starting a blocking GC Explicit
    12-04 16:45:36.941 I/art     (23188): Explicit concurrent mark sweep GC freed 5227(440KB) AllocSpace objects, 24(3MB) LOS objects, 62% free, 4MB/12MB, paused 302us total 31.346ms
    12-04 16:45:36.943 D/Mono    (23188): GC_TAR_BRIDGE bridges 362 objects 8823 opaque 2742 colors 362 colors-bridged 362 colors-visible 362 xref 0 cache-hit 0 cache-semihit 0 cache-miss 0 setup 0.11ms tarjan 9.11ms scc-setup 0.23ms gather-xref 0.01ms xref-setup 0.02ms cleanup 0.28ms
    12-04 16:45:36.943 D/Mono    (23188): GC_BRIDGE: Complete, was running for 35.88ms
    12-04 16:45:36.943 D/Mono    (23188): GC_MINOR: (Nursery full) time 18.20ms, stw 20.86ms promoted 187K major size: 4400K in use: 3752K los size: 13432K in use: 11411K
    12-04 16:45:36.973 W/cr_AwContents(23188): onDetachedFromWindow called when already detached. Ignoring
    12-04 16:45:36.979 I/cr_Ime  (23188): ImeThread is not enabled.
    
    using Xamarin.Forms;
    using MyApp.Droid;
    
    [assembly: ExportRenderer(typeof(WebView), typeof(CustomWebViewRenderer))]
    namespace MyApp.Droid
    {        
        using Android.OS;
        using global::Xamarin.Forms.Platform.Android;
        using Android.Content;
        using System;
    
        public class CustomWebViewRenderer : WebViewRenderer
        {        
            public CustomWebViewRenderer(Context context) : base(context)
            {
            }
    
            public IWebViewController GetElementController()
            {
                return Element;
            }
    
            protected override void OnElementChanged(ElementChangedEventArgs<global::Xamarin.Forms.WebView> e)
            {
                try
                {
                    base.OnElementChanged(e);
    
                    if (Control != null && e.NewElement != null)
                    {
                        SetupControlSettings();
                    }
                }
                catch (Exception ex)
                {
                    Logging.LogException(ex);
                }
            }           
    
            private void SetupControlSettings()
            {
                try
                {   
                    if (Build.VERSION.SdkInt >= BuildVersionCodes.JellyBeanMr1)
                    {
                        Control.Settings.MediaPlaybackRequiresUserGesture = false;
                        Control.Settings.JavaScriptEnabled = true;
                        Control.Settings.AllowContentAccess = true;
                        Control.Settings.DomStorageEnabled = true;
                    }
    
                    if ((int)Build.VERSION.SdkInt >= 19)
                    {
                        Control.SetLayerType(Android.Views.LayerType.Hardware, null);
                    }
                    else
                    {
                        Control.SetLayerType(Android.Views.LayerType.Software, null);
                    }
    
                    Control.Settings.SetAppCacheMaxSize(10 * 1024 * 1024);
                    Control.Settings.AllowFileAccess = true;
                    Control.Settings.AllowUniversalAccessFromFileURLs = true;
                    Control.Settings.SetAppCacheEnabled(true);
                    Control.Settings.CacheMode = Android.Webkit.CacheModes.NoCache;    
    
                    var webViewClient = new CustomWebViewClient(this, MyApp.GlobalVariables.AuthToken);               
    
                    Control.SetWebViewClient(webViewClient);
    
                }
                catch (Exception ex)
                {
                    Logging.LogException(ex);
                }
            }   
        }
    }
    
    using Android.Webkit;
    using System;
    using System.Net.Http;
    using Xamarin.Forms;
    
    namespace MyApp.Droid
    {
        public class CustomWebViewClient : WebViewClient
        {
            private readonly string authToken;
            private readonly CustomWebViewRenderer renderer;
            public CustomWebViewClient(CustomWebViewRenderer renderer, string authToken)
            {
                this.renderer = renderer;
                this.authToken = authToken;
            }
    
    
            public override bool ShouldOverrideUrlLoading(Android.Webkit.WebView view, IWebResourceRequest request)
            {
                WebNavigatingEventArgs args = new WebNavigatingEventArgs(WebNavigationEvent.NewPage, new UrlWebViewSource { Url = request.Url.ToString() }, request.Url.ToString());
                renderer.GetElementController().SendNavigating(args);
    
                if (args.Cancel)
                {
                    view.StopLoading();
                }
    
                return base.ShouldOverrideUrlLoading(view, request);
            }
    
    
            public override WebResourceResponse ShouldInterceptRequest(Android.Webkit.WebView view, IWebResourceRequest request)
            {
                try
                {
                    if (!string.IsNullOrWhiteSpace(authToken))
                    {
                        string authorizationValue = "Bearer " + authToken;    
                        using (HttpClient client = new HttpClient())
                        {    
                            request.RequestHeaders.Add("Authorization", authorizationValue);    
                            foreach(var h in request.RequestHeaders)
                            {
                                if (client.DefaultRequestHeaders.Contains(h.Key))
                                {
                                    client.DefaultRequestHeaders.Remove(h.Key);
                                }  
                                client.DefaultRequestHeaders.Add(h.Key,h.Value);                            
                            }    
                            HttpResponseMessage response = client.GetAsync(request.Url.ToString()).Result;
    
                            string encoding = response.Content.Headers.ContentEncoding.GetEnumerator().Current;
                            string contentType = response.Content.Headers.ContentType.ToString();
                            System.IO.Stream stream = response.Content.ReadAsStreamAsync().Result;
    
                            if (string.IsNullOrWhiteSpace(encoding) && contentType.Contains(";"))
                            {
                                string[] parts = contentType.Split(';');
                                contentType = parts[0].Trim();
                                encoding = parts[1].Trim();
                            }
    
                            WebResourceResponse result = new WebResourceResponse(contentType, encoding, stream);    
                            return result;
                        }
                    }
                }
                catch (Exception ex)
                {
                    Logging.LogException(ex);
                }
    
                return base.ShouldInterceptRequest(view, request);
            }
    
    
            public override void OnPageFinished(Android.Webkit.WebView view, string url)
            {
                try
                {
                    base.OnPageFinished(view, url);
    
                    UrlWebViewSource source = new UrlWebViewSource { Url = url };
                    WebNavigatedEventArgs args = new WebNavigatedEventArgs(WebNavigationEvent.NewPage, source, url, WebNavigationResult.Success);
                    renderer?.GetElementController()?.SendNavigated(args);
                }
                catch (Exception ex)
                {
                    Logging.LogException(ex);
                }
            } 
        }
    }
    
    编辑1: 这似乎是由于以下代码:

    HttpResponseMessage response = client.GetAsync(request.Url.ToString()).Result;
    
    如果我注释掉WebResourceResponse返回(因此它落在基本方法上),但将其保留在问题中,则问题仍然存在,但注释这一行可以使它工作

    编辑2: 情节越来越复杂。 问题站点使用信号器进行事件通知。 添加一些日志后,我可以看到WebViewClient开始加载 …/signalr/connect?transport=serverSentEvents&connectionToken=
    但永远不会结束,我猜这就是signar在服务器上的事件发生后才响应的方式。但是,这会阻止对WebViewClient的后续调用

    我对您所说的感到困惑,请您发布有关您的问题的更多详细信息,好吗?到底出了什么问题?我已经更新了一个例子。基本上,在访问特定站点之后,即使在重新加载页面之后,web视图也会停止工作(我认为Xamarin会恢复本机控件)。删除ShouldInterceptRequest中的代码修复了该问题,但删除了发送身份验证标头的功能。似乎与服务器上的Signal相关。请查看我的上述编辑可能的解决方案,但需要转换为C#我对您所说的感到困惑,请发布有关您问题的更多详细信息?到底出了什么问题?我已经更新了一个例子。基本上,在访问特定站点之后,即使在重新加载页面之后,web视图也会停止工作(我认为Xamarin会恢复本机控件)。删除ShouldInterceptRequest中的代码修复了此问题,但删除了发送身份验证标头的功能。似乎与服务器上的SignalR有关。请参见此处的“我的编辑”可能的解决方案,但需要转换为C#