C# Xamarin使用CustomWebViewClient形成Android Web视图在加载特定站点后停止工作
为了使用我的WebView发送身份验证标头,我有自定义WebViewRenderer和WebViewClient 可以为各种URL创建、加载、关闭并重新创建带有此控件的Xamarin forms页面,而不会出现任何问题。但是,在访问特定站点后,WebView将不再加载任何URL,并且根本不会调用ShouldInterceptRequest,即使在使用此控件关闭并创建新页面后也是如此 例如: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
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#