前言

在上一篇关于WebView的文章中,介绍了 WebView 的基本使用方法、WebView 页面处理和历史记录以及和 JS 调用本地代码的相关内容。今天就在上一篇文章的基础上,补充一些 WebView 的更深入的东西。

主要从如下几点来进行补充:

  1. 加载本地的 HTML 页面
  2. WebView 一些方法的讲解
  3. WebView 与 WebViewClient、WebChromeClient 的关系和作用
  4. WebViewClient 和 WebChromeCient 的方法介绍
  5. 错误码的处理
  6. 利用 WebView 来下载文件
  7. 远程注入漏洞的解决方案

加载本地 HTML 页面

前一篇文章中说到使用 WebView 需要开启 Internet 的权限,这个说法其实是不准确的。开启权限只是因为我们使用 loadUrl() 方法来获取 URL 的内容时需要联网。当我们选择加载本地的 HTML 页面(比如 assets 目录下的 HTML 文件或者使用 loadData() 方法加载一段 HTML 代码),并不需要网络权限。

加载 assets 目录下的 HTML 文件的方式和加载网页是一致的,都是使用 WebView 的 loadUrl() 方法。

mWebView.loadUrl("file:///android_asset/index.html");复制代码

WebView 部分方法详解

上篇文章在介绍 WebView 的历史记录时,列举了部分相关的方法并做了简单的描述。这部分主要补充几个 load 方法的介绍。

void loadData (String data, String mimeType, String encoding)

加载指定的 data 数据,有时需要加载的不是一个完整的网页,而是一个 HTML 片段。

data 表示给定编码的 String 类型 HTML 文本。mimeType 表示 data 的 MIMIE 类型,比如 “text/html”。encoding 表示 data 的编码格式。

这个方法中有两个地方需要注意:

  • JavaScript 有同源限制。意味着在此页面中运行的脚本无法访问任何不是通过 data 加载的数据。避免同源限制可以采用 loadDataWithBaseURL() 方法。
  • encoding 指定了 data 是使用 base64 编码还是使用 URL 编码。如果 data 是使用 base64 编码的,encoding 的值一定为 “base64”,如果是其他值的编码方式(包括 null),则默认使用 ASCII 编码方式。对于超出 ASCII 范围的字符比如 ‘#’,‘%’,'\','?',应该转化为 ”%+原字符的十六进制值“ 如 ‘%23’、‘%25’、‘%27’、‘%3f’。

示例如下:

String htmlString = "<html><title>this is title</title><body>this is web content.</body></html>";
mWebView.loadData(htmlString, "text/html","utf-8");复制代码

void loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding, String historyUrl)

这个方法比上面那个方法只多了两个参数,一个是 baseUrl,一个是 historyUrl。baseUrl 指定了 data 数据的基准地址。我们知道,在 HTML 代码中通常会有超链接或者图片,而这些链接或者图片地址通常使用的都是相对路径,如果没有一个基准,WebView 就无法访问到这些资源。

下面我们在 assets 目录下模拟网站资源的相对路径,来帮助我们更好地理解:

assets 目录下创建了 web 目录,里面有一张图片名为 logo.png

String htmlStr = "this is our logo:<img src="/logo.png"/>"
mWebView.loadData(htmlStr, "text/html","utf-8");
mWebView.loadDataWithBaseUrl("file:///android_asset/web",htmlstr,"text/html","utf-8",null);复制代码

使用 loadData() 方法无法加载图片,只有使用 loadDataWithBaseUrl() 方法才能加载。

void loadUrl(String url)

加载指定 URL 的内容。

void loadUrl(String url, Map additionalHttpHeaders)

加载指定的 URL 的内容,并携带 http header 数据。

void reload()

重新加载当前页面。页面中所有资源会重新加载。

WebSettings getSettings()

获取对这个 WebView 进行设置的 WebSettings 对象。

WebSettings 是管理 WebView 设置状态的类。当首次创建 WebView 时,它会获得一组默认设置。这些默认设置可以通过 WebSettings 的任何 getter 方法调用返回。从 WebView.getSettings() 获取的WebSettings 对象与 WebView 的使用寿命相关。如果 WebView 已被破坏,WebSettings 上的任何方法调用都将抛出一个 IllegalStateException 异常。

上一篇文章中提到 WebView 本身不支持 JavaScript,需要通过 WebSettings 来设置,即:

mWebView.getSettings().setJavaScriptEnabled(true);复制代码

这里介绍其他几个常用的 WebSettings 的 setter 方法,来对 WebView 进行设置。

setCacheMode(int mode)
设置是否支持缓存及缓存模式,默认值为 LOAD_DEFAULT。
setDefaultFontSize(int size)
设置默认的字体大小。有效值在 1~72 之间,默认值为 16。
setSupportZoom(boolean support)
设置是否支持缩放,默认支持。
setDisplayZoomControls(boolean enabled)
设置是否显示缩放按钮。默认值为 true。

WebView 与 WebViewClient、WebChromeClient 的关系和作用

在上篇文章中介绍了通过覆盖 WebViewClient 的shouldOverrideUrlLoading() 方法来使用当前应用展示页面,而不是通过手机浏览器来打开。这里就系统地讲讲 WebView 和 WebViewClient、WebChromeClient 的关系以及它们各自的作用。

WebView 在加载网页时会产生各种事件,并回调给应用程序以便在网页加载过程进行一些额外的处理。如果所有的工作都由 WebView 本身来完成,那么它的工作量就太大了,因此 Android 中引入了WebVIewClient 和 WebChromeClient 类来帮助 WebView 分担这些事件处理的回调。

WebView 负责加载网页以及网页渲染。
WebViewClient 的主要职责是帮助 WebView 处理各种通知、回调事件。
WebChromeClient 的作用是监听网页加载进度以及对网页标题,网页图标、JS 对话框进行处理等等。

WebViewClient 和 WebChromeCient 的方法介绍

WebViewClient 部分方法说明
//网页开始加载时的回调函数
void onPageStarted(WebView view, String url, Bitmap favicon)
//网页加载结束时的回调函数
void    onPageFinished(WebView view, String url)
//网页加载出错时的回调函数
void onReceivedError(WebView view, int errorCode, String description, String failingUrl)
//选择使用应用程序进行页面展示时需要覆盖该方法
boolean shouldOverrideUrlLoading(WebView view, String url)复制代码
WebChromeClient 部分方法说明
//接收到页面 title 时回调,获取到 title 可以设置到应用的 ActionBar 上。
void onReceivedTitle(WebView view, String title)
//接收到页面的图标时回调
void onReceivedIcon(WebView view, Bitmap icon)
//当页面加载进度发生变化时回调,可以设置一个 ProgressBar 来显示加载进度
void onProgressChanged(WebView view, int newProgress)
//当页面弹出 Javascript 警报对话框时调用
//客户端可以设置是否对这个对话框进行处理
boolean    onJsAlert(WebView view, String url, String message, JsResult result)
//当页面弹出 Javascript 确认对话框时调用
//客户端可以设置是否对这个对话框进行处理
boolean    onJsConfirm(WebView view, String url, String message, JsResult result)
//当页面弹出 Javascript 提示对话框时调用
//客户端可以设置是否对这个对话框进行处理
boolean    onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result)复制代码

错误码的处理

在浏览网页时经常会遇到 404 错误,也就是访问的页面不存在,这里就简单介绍一下如何处理 WebView 中的 404 错误。对错误码的处理需要覆盖 WebViewClient 类中的 onReceiveError() 方法,对于 404 错误,有两个常用的处理方法:

第一,加载一个 assets 目录下的写好的 404 页面

class MyWebViewClient extends WebViewClient{@Overridepublic void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {view.loadUrl("file:///android_asset/404.html");}
}
mWebView.setWebViewClient(new MyWebViewClient());复制代码

第二,使用 Android 中的 ImageView 或者 TextVIew 来显示错误信息。

TextView tvError = (TextView) findViewById(R.id.tv_error);
class MyWebViewClient extends WebViewClient{@Overridepublic void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {mWebView.setVisibility(View.GONE);tvError.setVisibility(View.VISIBLE);tvError.setText("something goes wrong");}
}
mWebView.setWebViewClient(new MyWebViewClient());复制代码

利用 WebView 下载文件

使用 WebView 进行文件下载同样有两种常用的方法,一种是使用隐式 Intent,调用系统浏览器进行下载任务;一种是获取到下载文件的 URL,创建线程进行异步下载。

我们这里就简单介绍一下使用隐式 Intent 的方法来下载文件,第二种方法将在后面一篇关于 HttpUrlConnection 的文章中进行介绍。

WebView 有一个 setDownloadListener() 方法,当 URL 的内容不能被 WebView 引擎渲染时(文件),应该被下载。需要新建一个 DownloadListener 的实现类。在该实现类的 onDownloadStart() 方法中,可以获取到当前下载文件的 URL。之后就可以通过隐式 Intent 来调用系统浏览器进行下载。

class MyDownloadListener implements DownloadListener{@Overridepublic void onDownloadStart(String url, String userAgent,String contentDisposition,String mimetype, long contentLength) {if(url.endsWith(".apk")){Uri uri = Uri.parse(url);Intent i = new Intent(Intent.ACTION_VIEW, uri);startActivity(i);}}
}
mWebView.loadUrl("http://www.wandoujia.com/");
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.setDownloadListener(new MyDownloadListener());复制代码

上述代码实现了在豌豆荚网站中下载 apk 文件的效果。

远程注入漏洞的解决方案

上篇文章中讲到了在 Android 4.2 版本之前使用 addJavascriptInterface() 方法会存在安全隐患,攻击者可以通过反射机制来对客户端进行攻击。那么在 4.2 之前,这个漏洞有没有什么解决方案呢?

答案是有的,在网上看了一些资料,找到了对应的方案。在上面的 WebChromeClient 的方法介绍时提到了一个 onJsPrompt() 方法,这个方法在 JS 调用 prompt 方法时会在本地回调。通过这个方法,JS 能把信息(文本)传递到 Java,而 Java 也能把信息(文本)传递到 JS 中。客户端和网页前端的开发者之间协商好一个小型的协议,通过 onJsPrompt() 将远程调用所需的信息按照协议封装成文本发给客户端,客户端根据协议解析这段文本,提取出所需的信息,利用反射机制进行本地的方法调用,如果有返回值就将返回值通过onJsPrompt() 传递给 JS。就能够实现 JS 远程调用 Java 代码了。

这样的解决方案我也只是一知半解,所以只能介绍到这里,在参考资料中会有更加详细的介绍。

结束语

这篇文章结束后,对于 WebView 的一些常见的方法和这些方法背后的原理都略作介绍。由于是在学习阶段,很多知识和原理并不能做到多么深入,但是通过写这两篇关于 WebView 的文章,去看了很多的博客,研究了一番官方文档,收获还是非常大的。接下来就要去学习 HttpUrlConnection 的相关用法,下篇文章见。

参考文章

以下是在学习过程中看的一些博客,不断向这些作者学习,努力深入知识点,然后写出尽量高质量的博客。

blog.csdn.net/leehong2005…
Android WebView 的 Js 对象注入漏洞解决方案
blog.csdn.net/typename/ar…
blog.csdn.net/typename/ar…
blog.csdn.net/typename/ar…
Android WebView 开发详解(共3篇)
www.jianshu.com/p/93cea79a2…
JS 与 WebView 交互存在的一些问题

Android 网络编程系列(3)WebView 详解相关推荐

  1. Java网络编程 Socket、ServerSocket 详解,方法介绍及完整代码示例

    Java网络编程 Socket.ServerSocket 详解,方法介绍及完整代码示例 概念 什么是网络编程? 网络编程是指编写运行在多个设备(计算机)的程序,这些设备通过网络连接起来.当这些通过网络 ...

  2. 《Android 网络开发与应用实战详解》——1.3节搭建Android应用开发环境

    本节书摘来自异步社区<Android 网络开发与应用实战详解>一书中的第1章,第1.3节搭建Android应用开发环境,作者 王东华,更多章节内容可以访问云栖社区"异步社区&qu ...

  3. 《Android 网络开发与应用实战详解》——2.3节Android系统架构

    本节书摘来自异步社区<Android 网络开发与应用实战详解>一书中的第2章,第2.3节Android系统架构,作者 王东华,更多章节内容可以访问云栖社区"异步社区"公 ...

  4. 《Android 网络开发与应用实战详解》——2.1节简析Android安装文件

    本节书摘来自异步社区<Android 网络开发与应用实战详解>一书中的第2章,第2.1节简析Android安装文件,作者 王东华,更多章节内容可以访问云栖社区"异步社区" ...

  5. 网络架构系列1--TCP/IP详解

    不诗意的女程序媛不是好厨师~ 转载请注明出处,From李诗雨-[https://blog.csdn.net/cjm2484836553/article/details/103930596] <网 ...

  6. Android网络编程系列 一 Socket抽象层

    在<Android网络编程>系列文章中,前面已经将Java的通信底层大致的描述了,在我们了解了TCP/IP通信族架构及其原理,接下来我们就开始来了解基于tcp/ip协议层的Socket抽象 ...

  7. Spark核心编程系列(一)——RDD详解

    目录 Spark核心编程系列--RDD详解(一) RDD概念 RDD与IO之间的关系 RDD的核心属性 RDD执行原理 基础编程 RDD创建 RDD的并行度与分区 参考 Spark核心编程系列--RD ...

  8. C语言网络编程:accept函数详解

    文章目录 前言 函数描述 代码实例 如何得到客户端的IP 和 端口号 前言 当使用tcp服务器使用socket创建通信文件描述符,bind绑定了文件描述符,服务器ip和端口号,listen将服务器端的 ...

  9. Android 网络编程系列(5)Volley 网络框架入门

    前言 上篇文章中我们对 HttpUrlConnection 的相关用法稍作介绍,可以看到如果不对它进行封装,那么每次使用时就必须写很多重复的代码,并且需要自己创建线程进行网络连接,获取到响应结果后还需 ...

最新文章

  1. java聊天程序步骤解析_java网络之基于UDP的聊天程序示例解析
  2. POJ 3276 Face The Right Way 反转
  3. 2.1 Hadoop概述
  4. EasyUi通过POI 实现导出xls表格功能
  5. ES6 Set结构和Map结构(上)
  6. mo汇编指令_汇编指令(汇编指令详解)
  7. ubuntu16.04安装及卸载anaconda3
  8. Rockchip BT.656 TX 和 BT.1120 TX 开发指南
  9. BootStrap之导航条navigationBar
  10. 通过Nginx转发的Fastdfs文件地址,在浏览器页面上会直接打开而不提示下载框的解决方案
  11. v-model的radio checkbox以及键盘修饰符使用
  12. 微信小程序开发学习小结之tabBar组件
  13. 神经网络和深度神经网络,深度神经网络类型包括
  14. CST学习------网格类型及设置方法和技巧
  15. 王子救公主 (计蒜客)一道简单DFS
  16. html5自闭合标签有哪些,自闭合标签与伪元素
  17. 程序员必读:“五险一金”详解!
  18. 可视化高并发,高处理任务异步编排设计
  19. 用Java模拟微信红包的实现
  20. 近期岗位急招 感兴趣的可以来聊

热门文章

  1. python phpstudy_Java、python及phpstudy的环境配置
  2. 2021-11-18可变参数
  3. java jsoup html_使用JAVA中的JSOUP从HTML中提取CSS样式
  4. 人工智能学习--文本识别实践-tesseract-ocr
  5. Python os和os.path的基础知识与常用操作
  6. 程序员的能力拓展模型
  7. 在线html转ipa,iphone在线安装 ipa 应用:利用 itms-services 协议实现 iOS 应用程序在线安装功能...
  8. carsim8.02和matlab2016b的联合仿真,找不到carsim s-function的解决办法
  9. Python 实现链表和二叉树
  10. c++17(30)-文件读写(1)