分享web开发知识

注册/登录|最近发布|今日推荐

主页 IT知识网页技术软件开发前端开发代码编程运营维护技术分享教程案例
当前位置:首页 > 前端开发

WebView使用详解(二)——WebViewClient与常用事件监听

发布时间:2023-09-06 01:10责任编辑:顾先生关键词:Web
  • 登录|注册
关闭

启舰

当乌龟有了梦想……

  • 目录视图
  • 摘要视图
  • 订阅
异步赠书:Kotlin领衔10本好书免费直播:AI时代,机器学习如何入门?程序员8月书讯每周荐书:Java Web、Python极客编程(评论送书)

WebView使用详解(二)——WebViewClient与常用事件监听

2016-05-28 11:2420083人阅读评论(13)收藏举报
分类:

版权声明:本文为博主原创文章,未经博主允许不得转载。

目录(?)[+]

前言:生活的艰难,更会激发对梦想的渴望,但艰难的生活却往往会成为梦想的绊脚石

相关文章:
1、《WebView使用详解(一)——Native与JS相互调用(附JadX反编译)》
2、《WebView使用详解(二)——WebViewClient与常用事件监听》
3、《WebView使用详解(三)——WebChromeClient与LoadData补充》

上篇给大家简单讲了Webview中Native代码与JS相互调用的方法,这篇我们再讲讲有关各种拦截与处理的东东。

一、WebViewClient

1、概述

前面我们虽然实现了交互,但可能我们会有一个很简单的需求,就是在开始加载网页的时候显示进度条,加载结束以后隐藏进度条,这要怎么做?
这些简单的需求,Android开发的老人们肯定都已经想到了,这些有关各种事件的回调都被封装在WebViewClient类中了,在WebViewClient中有各种的回调方法,就是在某个事件发生时供我们监听
使用方法如下:
[java]view plaincopy
  1. mWebView.setWebViewClient(newWebViewClient(){
  2. @Override
  3. publicvoidonPageStarted(WebViewview,Stringurl,Bitmapfavicon){
  4. super.onPageStarted(view,url,favicon);
  5. Log.d(TAG,"onPageStarted");
  6. }
  7. @Override
  8. publicvoidonPageFinished(WebViewview,Stringurl){
  9. super.onPageFinished(view,url);
  10. Log.d(TAG,"onPageFinished");
  11. }
  12. });
直接调用WebView.setWebViewClient方法即可设置WebViewClient回调,这里重写的两个函数,onPageStarted会在WebView开始加载网页时调用,onPageFinished会在加载结束时调用。这两个函数就可以完成我们开篇时的需求:在开始加载时显示进度条,在结束加载时隐藏进度条。

2、WebViewClient中函数概述

在WebViewClient中除了上面我们列举出的onPageStarted、onPageFinished还有很多其它函数,分别是:
[java]view plaincopy
  1. /**
  2. *在开始加载网页时会回调
  3. */
  4. publicvoidonPageStarted(WebViewview,Stringurl,Bitmapfavicon)
  5. /**
  6. *在结束加载网页时会回调
  7. */
  8. publicvoidonPageFinished(WebViewview,Stringurl)
  9. /**
  10. *拦截url跳转,在里边添加点击链接跳转或者操作
  11. */
  12. publicbooleanshouldOverrideUrlLoading(WebViewview,Stringurl)
  13. /**
  14. *加载错误的时候会回调,在其中可做错误处理,比如再请求加载一次,或者提示404的错误页面
  15. */
  16. publicvoidonReceivedError(WebViewview,interrorCode,Stringdescription,StringfailingUrl)
  17. /**
  18. *当接收到https错误时,会回调此函数,在其中可以做错误处理
  19. */
  20. publicvoidonReceivedSslError(WebViewview,SslErrorHandlerhandler,SslErrorerror)
  21. /**
  22. *在每一次请求资源时,都会通过这个函数来回调
  23. */
  24. publicWebResourceResponseshouldInterceptRequest(WebViewview,
  25. Stringurl){
  26. returnnull;
  27. }
上面的方法比较多,我们一个个来看

3、WebViewClient之onPageStarted与onPageFinished

onPageStarted:通知主程序页面当前开始加载。该方法只有在加载main frame时加载一次,如果一个页面有多个frame,onPageStarted只在加载main frame时调用一次。也意味着若内置frame发生变化,onPageStarted不会被调用,如:在iframe中打开url链接。
onPageFinished:通知主程序页面加载结束。方法只被main frame调用一次。
我们就利用上面的想法来举个例子:开始加载时显示加载圆圈,结束加载时隐藏加载圆圈
[java]view plaincopy
  1. publicclassMyActivityextendsActivity{
  2. privateWebViewmWebView;
  3. privateProgressDialogmProgressDialog;
  4. privateStringTAG="qijian";
  5. @Override
  6. publicvoidonCreate(BundlesavedInstanceState){
  7. super.onCreate(savedInstanceState);
  8. setContentView(R.layout.main);
  9. mWebView=(WebView)findViewById(R.id.webview);
  10. mProgressDialog=newProgressDialog(this);
  11. mWebView.getSettings().setJavaScriptEnabled(true);
  12. mWebView.loadUrl("http://blog.csdn.net/harvic880925");
  13. mWebView.setWebViewClient(newWebViewClient(){
  14. @Override
  15. publicbooleanshouldOverrideUrlLoading(WebViewview,Stringurl){
  16. mWebView.loadUrl(url);
  17. returntrue;
  18. }
  19. @Override
  20. publicvoidonPageStarted(WebViewview,Stringurl,Bitmapfavicon){
  21. super.onPageStarted(view,url,favicon);
  22. mProgressDialog.show();
  23. }
  24. @Override
  25. publicvoidonPageFinished(WebViewview,Stringurl){
  26. super.onPageFinished(view,url);
  27. mProgressDialog.hide();
  28. }
  29. });
  30. }
  31. }
效果图如下:

从效果图中可以明显看出,在加载页面的时候会显示圆形加载框,在加载成功以后会隐藏加载框。

4、WebViewClient之shouldOverrideUrlLoading

该函数的完整声明如下:
[java]view plaincopy
  1. publicbooleanshouldOverrideUrlLoading(WebViewview,Stringurl)
这个函数会在加载超链接时回调过来;所以通过重写shouldOverrideUrlLoading,可以实现对网页中超链接的拦截;
返回值是boolean类型,表示是否屏蔽WebView继续加载URL的默认行为,因为这个函数是WebView加载URL前回调的,所以如果我们return true,则WebView接下来就不会再加载这个URL了,所有处理都需要在WebView中操作,包含加载。如果我们return false,则系统就认为上层没有做处理,接下来还是会继续加载这个URL的。WebViewClient默认就是return false的:
[java]view plaincopy
  1. publicbooleanshouldOverrideUrlLoading(WebViewview,Stringurl){
  2. returnfalse;
  3. }

(1)、如何在WebView中加载在线网址

在上一篇中,我们提到,如果要在WebView中加载在线网址,必须重写WebViewClient
现在网上铺天盖地的都是重写shouldOverrideUrlLoading来将URL加载进WebView,但在用多了WebView以后会发现,直接下面这样写,就可以实现在WebVIew中加载网页:
[java]view plaincopy
  1. publicclassMyActivityextendsActivity{
  2. privateWebViewmWebView;
  3. privateProgressDialogmProgressDialog;
  4. privateStringTAG="qijian";
  5. @Override
  6. publicvoidonCreate(BundlesavedInstanceState){
  7. super.onCreate(savedInstanceState);
  8. setContentView(R.layout.main);
  9. mWebView=(WebView)findViewById(R.id.webview);
  10. mProgressDialog=newProgressDialog(this);
  11. mWebView.getSettings().setJavaScriptEnabled(true);
  12. mWebView.setWebViewClient(newWebViewClient());
  13. mWebView.loadUrl("http://blog.csdn.net/harvic880925");
  14. }
  15. }
效果图如下:

从效果图中可以看出即仅仅设置WebViewClient对象,使用它的默认回调就可以实现在WebView中加载在线URL了:

[plain]view plaincopy
  1. mWebView.setWebViewClient(newWebViewClient());

(2)、shouldOverrideUrlLoading用途

由于每次超链接在加载前都会先走shouldOverrideUrlLoading回调,所以我们如果想拦截某个URL,将其转换成其它URL可以在这里做。
比如,我们拦截所有包含“blog.csdn.net”的地址,将其替换成”www.baidu.com”:
效果图如下:

代码如下:

[java]view plaincopy
  1. publicclassMyActivityextendsActivity{
  2. privateWebViewmWebView;
  3. privateProgressDialogmProgressDialog;
  4. privateStringTAG="qijian";
  5. @Override
  6. publicvoidonCreate(BundlesavedInstanceState){
  7. super.onCreate(savedInstanceState);
  8. setContentView(R.layout.main);
  9. mWebView=(WebView)findViewById(R.id.webview);
  10. mProgressDialog=newProgressDialog(this);
  11. mWebView.getSettings().setJavaScriptEnabled(true);
  12. mWebView.setWebViewClient(newWebViewClient(){
  13. @Override
  14. publicbooleanshouldOverrideUrlLoading(WebViewview,Stringurl){
  15. if(url.contains("blog.csdn.net")){
  16. view.loadUrl("http://www.baidu.com");
  17. }else{
  18. view.loadUrl(url);
  19. }
  20. returntrue;
  21. }
  22. });
  23. mWebView.loadUrl("http://blog.csdn.net/harvic880925");
  24. }
  25. }
最关键的位置在:
[java]view plaincopy
  1. publicbooleanshouldOverrideUrlLoading(WebViewview,Stringurl){
  2. if(url.contains("blog.csdn.net")){
  3. view.loadUrl("http://www.baidu.com");
  4. }else{
  5. view.loadUrl(url);
  6. }
  7. returntrue;
  8. }
如果在当前webview加载的url中包含“blog.csdn.net”,则将其转换成”www.baidu.com”
这里需要非常注意的是:如果我们在shouldOverrideUrlLoading中return true,就表示告诉系统我们已经拦截了URL并做处理,不需要再触发系统默认的行为()在WebView中加载URL;所以对于其它URL我们需要在else里重新调用view.loadUrl(url)来加载;不然WebView将会白屏,因为这个URL根本就没有加载进WebView,在shouldOverrideUrlLoading这就被我们拦截掉了。
那么问题来了,在我们return true了以后,WebView还会请求网络吗?我们来抓下请求:

从请求中可以看到,我们虽然拦截了”http://blog.csdn.net/harvic880925“但是仍然还是会请求网络的。只是请求以后结果并没有通过WebView加载。
那问题来了,如果我们return false呢,如果我们return false的话,是不需要else语句的,因为系统默认会加载这个URL,所以上面的语句与下面的意义相等:
[java]view plaincopy
  1. mWebView.setWebViewClient(newWebViewClient(){
  2. @Override
  3. publicbooleanshouldOverrideUrlLoading(WebViewview,Stringurl){
  4. if(url.contains("blog.csdn.net")){
  5. view.loadUrl("http://www.baidu.com");
  6. }
  7. returnfalse;
  8. }
  9. }
所以相对而言,我们使用return false好像更方便,只需要对需要拦截的URL进行拦截,拦截以后,让WebView处理默认操作即可。
所以结论来了:
在利用shouldOverrideUrlLoading来拦截URL时,如果return true,则会屏蔽系统默认的显示URL结果的行为,不需要处理的URL也需要调用loadUrl()来加载进WebVIew,不然就会出现白屏;如果return false,则系统默认的加载URL行为是不会被屏蔽的,所以一般建议大家return false,我们只关心我们关心的拦截内容,对于不拦截的内容,让系统自己来处理即可。

5、WebViewClient之onReceivedError

onReceivedError的完整声明如下:
[java]view plaincopy
  1. publicvoidonReceivedError(WebViewview,interrorCode,Stringdescription,StringfailingUrl)
加载错误的时候会产生这个回调,在其中可做错误处理,比如我们可以加载一个错误提示页面
这里有四个参数:
  • WebView view:当前的WebView实例
  • int errorCode:错误码
  • String description:错误描述
  • String failingUrl:当前出错的URL
我们可以先写一个错误提示的本地页面:(error.html)
[html]view plaincopy
  1. <!DOCTYPEhtml>
  2. <htmllang="en">
  3. <head>
  4. <metacharset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. <h1>啊哈,出错了……</h1>
  9. </body>
  10. </html>
然后在加载返回错误时,重新加载错误页面
[java]view plaincopy
  1. mWebView.setWebViewClient(newWebViewClient(){
  2. @Override
  3. publicvoidonReceivedError(WebViewview,interrorCode,Stringdescription,StringfailingUrl){
  4. super.onReceivedError(view,errorCode,description,failingUrl);
  5. mWebView.loadUrl("file:///android_asset/error.html");
  6. }
  7. });
效果图如下:

源码在文章底部给出

6、WebViewClient之onReceivedSslError

我们知道HTTPS协议是通过SSL来通信的,所以当使用HTTPS通信的网址(以https://开头的网站)出现错误时,就会通过onReceivedSslError回调通知过来,它的函数声明为:
[java]view plaincopy
  1. /**
  2. *当接收到https错误时,会回调此函数,在其中可以做错误处理
  3. */
  4. publicvoidonReceivedSslError(WebViewview,SslErrorHandlerhandler,SslErrorerror)
  • WebView view:当前的WebView实例
  • SslErrorHandler handler:当前处理错误的Handler,它只有两个函数SslErrorHandler.proceed()和SslErrorHandler.cancel(),SslErrorHandler.proceed()表示忽略错误继续加载,SslErrorHandler.cancel()表示取消加载。在onReceivedSslError的默认实现中是使用的SslErrorHandler.cancel()来取消加载,所以一旦出来SSL错误,HTTPS网站就会被取消加载了,如果想忽略错误继续加载就只有重写onReceivedSslError,并在其中调用SslErrorHandler.proceed()
  • SslError error:当前的的错误对象,SslError包含了当前SSL错误的基本所有信息,大家自己去看下它的方法吧,这里就不再展开了。

示例(1)、默认加载SSL出错的网站——出现空白页面

我们先举个例子来看下默认情况下加载SSL有错的网站,WebView的表现是怎样的:(12306是通过Https协议来传输的,但是它的SSL证书是有问题的,所以我们就以12306网站为例)
[java]view plaincopy
  1. publicclassMyActivityextendsActivity{
  2. privateWebViewmWebView;
  3. privateProgressDialogmProgressDialog;
  4. privateStringTAG="qijian";
  5. @Override
  6. publicvoidonCreate(BundlesavedInstanceState){
  7. super.onCreate(savedInstanceState);
  8. setContentView(R.layout.main);
  9. mWebView=(WebView)findViewById(R.id.webview);
  10. mProgressDialog=newProgressDialog(this);
  11. mWebView.getSettings().setJavaScriptEnabled(true);
  12. mWebView.setWebViewClient(newWebViewClient(){
  13. @Override
  14. publicvoidonReceivedSslError(WebViewview,SslErrorHandlerhandler,SslErrorerror){
  15. super.onReceivedSslError(view,handler,error);
  16. Log.e(TAG,"sslError:"+error.toString());
  17. }
  18. mWebView.loadUrl("https://www.12306.cn/");
  19. }
  20. }
在这里仅仅重写onReceivedSslError,并调用super.onReceivedSslError(view, handler, error);来调用默认的处理方式,然后把错误日志打出来:

错误日志如下:

示例(2)、使用SslErrorHandler.proceed()来继续加载

[java]view plaincopy
  1. mWebView.setWebViewClient(newWebViewClient(){
  2. @Override
  3. publicvoidonReceivedSslError(WebViewview,SslErrorHandlerhandler,SslErrorerror){
  4. //一定要注释掉!
  5. //super.onReceivedSslError(view,handler,error);
  6. handler.proceed();
  7. Log.e(TAG,"sslError:"+error.toString());
  8. }
  9. });
这里做了两个改变:
第一:注释掉super.onReceivedSslError(view, handler, error);,取消系统的默认行为,我们看源码,可以发现在WebViewClient中onReceivedSslError的默认实现是这样的:
[java]view plaincopy
  1. publicvoidonReceivedSslError(WebViewview,SslErrorHandlerhandler,
  2. SslErrorerror){
  3. handler.cancel();
  4. }
所以默认是取消继续加载的,所以我们必须注释掉super.onReceivedSslError(view, handler, error)来取消这个默认行为!
第二:调用handler.proceed();来忽略错误继续加载页面。
所以此时的效果图为:

示例(3):在SSL发生错误时,onReceivedError会被回调吗?——不会

大家可能还有一个疑问:当SSL发生错误时,我们说会回调onReceivedSslError,我们前面还说了一个出错时会回调的函数:onReceivedError,那么问题来了,当出现SSL错误时onReceivedError会被回调吗?
答案是不会的,我们来做个实验:
[java]view plaincopy
  1. mWebView.setWebViewClient(newWebViewClient(){
  2. @Override
  3. publicvoidonReceivedSslError(WebViewview,SslErrorHandlerhandler,SslErrorerror){
  4. //super.onReceivedSslError(view,handler,error);
  5. handler.proceed();
  6. Log.e(TAG,"sslError:"+error.toString());
  7. }
  8. @Override
  9. publicvoidonReceivedError(WebViewview,interrorCode,Stringdescription,StringfailingUrl){
  10. super.onReceivedError(view,errorCode,description,failingUrl);
  11. Log.e(TAG,"onReceivedError:"+errorCode+""+description);
  12. }
  13. });

在代码中我们同时使用onReceivedSslError和onReceivedError来接收错误,看下在出错时,哪个函数中会打出LOG,结果的日志如下:

从日志中明显可以看出,只有onReceivedSslError的接收日志,所以在SSL出错时,是不会触发onReceivedError回调的
所以对于onReceivedSslError结论来了:
当出现SSL错误时,WebView默认是取消加载当前页面,只有去掉onReceivedSslError的默认操作,然后添加SslErrorHandler.proceed()才能继续加载出错页面
当HTTPS传输出现SSL错误时,错误会只通过onReceivedSslError回调传过来

7、WebViewClient之shouldInterceptRequest

在每一次请求资源时,都会通过这个函数来回调,比如超链接、JS文件、CSS文件、图片等,也就是说浏览器中每一次请求资源时,都会回调回来,无论任何资源!但是必须注意的是shouldInterceptRequest函数是在非UI线程中执行的,在其中不能直接做UI操作,如果需要做UI操作,则需要利用Handler来实现,该函数声明如下:
[java]view plaincopy
  1. publicWebResourceResponseshouldInterceptRequest(WebViewview,
  2. Stringurl){
  3. returnnull;
  4. }
该函数会在请求资源前调用,我们可以通过返回WebResourceResponse的处理结果来让WebView直接使用我们的处理结果。如果我们不想处理,则直接返回null,系统会继续加载该资源。
利用这个特性,我们可以解决一个需求:假如网页中需要加载本地的图片,我们就可以通过拦截shouldInterceptRequest,并返回结果即可
比如下面的一段HTML代码中,img字段加载图片的地址是:http://localhost/qijian.png,这是我自定义的一个网址,在Android中,当发现要加载这个地址的资源时,我们将它换成本地的图片
[html]view plaincopy
  1. <!DOCTYPEhtml>
  2. <htmllang="en">
  3. <head>
  4. <metacharset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. <h1>欢迎光临启舰的blog</h1>
  9. <imgsrc="http://localhost/qijian.png"/>
  10. </body>
  11. </html>
然后是Native代码:
[java]view plaincopy
  1. publicclassMyActivityextendsActivity{
  2. &nb
我的编程学习网——分享web前端后端开发技术知识。 垃圾信息处理邮箱 tousu563@163.com 网站地图
icp备案号 闽ICP备2023006418号-8 不良信息举报平台 互联网安全管理备案 Copyright 2023 www.wodecom.cn All Rights Reserved