前言
通常我们在自己开发的 APP 中打开网页无非两种方法: 一是跳转到系统自带的浏览器,二是使用 WebView 控件加载页面。使用 WebView 控件的好处就是可以通过各种 api 接口来定制各种行为,常用的几个设置地方为 WebSettings、JavaScriptInterface、WebViewClient 和WebChromeClient。平时出现的问题都可以通过修改这些设置来解决。
问题总结
1、使用了 WebView 还是跳转到了系统自带的浏览器?
很简单的解决方法,为你的 webview 设置一个新的 WebViewClient。
1 | webView.setWebViewClient(new WebViewClient(){ |
1 | // 或者直接添加,效果是一样的 |
2、获取网页的标题和图标
通过 WebChromeClient 可以获取到这些信息。
1 | webView.setWebChromeClient(new WebChromeClient() { |
但是,这里有个问题,当通过 webView.goBack() 方式返回上一级Web页面的时候不会触发这个方法,因此会导致标题无法跟随历史记录返回上一级页面。所以需要在 onPageFinished() 中对界面标题重新设置。
1 | webView.setWebViewClient(new WebViewClient(){ |
3、返回键实现网页的后退键
在 WebView 中可以通过 goBack() 方法后退到历史记录的上一项。
1 | // 在 Actvity 中监听返回键按钮 |
4、设置 WebView 的 header
在 WebView 的 loadUrl() 方法中传入 Header 参数即可。
1 | public void loadURLWithHTTPHeaders() { |
5、设置 WebView 的 User-Agent
不要试图在 Header 里面去修改,而是在 WebSettings 修改
1 | webView.getSettings().setUserAgentString("Mozilla/5.0 (Windows NT 10.0; WOW64; rv:50.0) Gecko/20100101 Firefox/50.0"); |
6、如何设置 WebView 的缓存
当需要本地缓存网页的时候就需要打开 WebViewSettings 的缓存开关,这样子当下次进到该页面无网络的情况下也能打开页面。
1 | WebSettings settings = webView.getSettings(); |
当我们加载Html时候,会在我们data/应用package下生成database与cache两个文件夹:
我们请求的Url记录是保存在webviewCache.db里,而url的内容是保存在webviewCache文件夹下.
WebView中存在着两种缓存:网页数据缓存(存储打开过的页面及资源)、H5缓存(即AppCache)。
网页数据缓存
WebSettings可设置缓存方式:
①LOAD_DEFAULT:默认设置,当有缓存而且没有过期使用缓存,否则使用网络数据。
②LOAD_CACHE_ELSE_NETWORK:只要有缓存就使用缓存,即使已经过期,否则使用网络数据。
③LOAD_NO_CACHE:不适用缓存,只加载网络数据。
④LOAD_CACHE_ONLY:不使用网络,只使用缓存数据。
⑤LOAD_CACHE_NORMAL: API level 17中已经废弃, 从API level 11开始作用同LOAD_DEFAULT模式
方法调用:
webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);
AppCache
查阅相关资料,总结如下:
①AppCache简介:对app内存缓存的方案,具体表现为当请求某个文件时不是从网络获取该文件,而是从本地获取。
②AppCache的好处:离线浏览 ,速度 - 已缓存资源加载得更快,减少服务器负载 - 浏览器将只从服务器下载更新过或更改过的资源。
在WebView中使用相关api
1、缓存构成
根据setAppCachePath(String appCachePath)提供的路径,在H5使用缓存过程中生成的缓存文件。
/data/data/package_name/cache/
/data/data/package_name/database/webview.db
/data/data/package_name/database/webviewCache.db
综合可以得知 webview 会将我们浏览过的网页url已经网页文件(css、图片、js等)保存到数据库表中
2、缓存模式
无模式选择,通过setAppCacheEnabled(boolean flag)设置是否打开。默认关闭,即,H5的缓存无法使用。
3、清除缓存
找到调用setAppCachePath(String appCachePath)设置缓存的路径,把它下面的文件全部删除就OK了。
4、控制大小
通过setAppCacheMaxSize(long appCacheMaxSize)设置缓存最大容量,默认为Max Integer。
同时,可能通过覆盖WebChromeClient.onReachedMaxAppCacheSize(long requiredStorage, long quota, WebStorage.QuotaUpdater quotaUpdater)来设置缓存超过先前设置的最大容量时的策略。
如:www.taobao.com的cache-control为no-cache,在模式LOAD_DEFAULT下,无论如何都会从网络上取数据,如果没有网络,就会出现错误页面;在LOAD_CACHE_ELSE_NETWORK模式下,无论是否有网络,只要本地有缓存,都使用缓存。本地没有缓存时才从网络上获取。
www.360.com.cn的cache-control为max-age=60,在两种模式下都使用本地缓存数据。
总结:根据以上两种模式,建议缓存策略为,判断是否有网络,有的话,使用LOAD_DEFAULT,无网络时,使用LOAD_CACHE_ELSE_NETWORK。
设置WebView 缓存模式
1 | private void initWebView() { |
清除缓存
1 | /** |
完整代码
1 | package com.example.webviewtest; |
7、无法下载文件?
在自己写的 WebView 下是无法直接下载文件,需要自己监听下载事件并对下载的动作进行处理。
1 | /** |
8、无法打开文件选择器?
通过重写 WebChromeClient 来实现点击 来打开系统文件选择器。
一个完整的Activity示例
1 | public class MainActivity extends AppCompatActivity { |
9、怎么为 WebView 的加载添加进度条
这里的 onPageFinished() 有个问题,不能在这里监听页面是否加载完毕(我自己测试的时候,好像在重定向和加载完 iframes 时都会调用这个方法)。
把页面加载完毕的判断放在 onProgressChanged() 里可能会更为准确。
1 | webView.setWebChromeClient(new WebChromeClient() { |
10、怎样对页面进行 Js 注入?
首先你要在 WebView 开启 JavaScript,然后搭建桥梁
1 | WebSettings webSettings = webView.getSettings(); |
WebAppBridge的代码
1 | public class WebAppBridge { |
简单的说就是向网页注入一段 js, 在这段 js 里面设置回调到java中的方法 getResult(),由 WebAppBridge.getResult 来回收。
其中js的核心代码为:
1 | oauth.getResult(str); |
其中 oauth 这个名称要与 webView.addJavascriptInterface()方法的第二个参数一样。
具体的代码可以参考这个项目中写的 js 注入逻辑 OauthDialog
地址:https://github.com/cpacm/MoeMusic/blob/master/app/src/main/java/com/cpacm/moemusic/ui/widgets/dialogs/OauthDialog.java
11、如何手动添加 Cookie
需要获得 CookieManager 的对象并将 cookie 设置进去。
从服务器的返回头中取出 cookie 根据Http请求的客户端不同,获取 cookie 的方式也不同,请自行获取。
1 | /** |
删除 Cookie 的方法
1 | /** |
12、如何使 HTML5 video 在 WebView 全屏显示
当网页全屏播放视频时会调用 WebChromeClient.onShowCustomView() 方法,所以可以通过将 video 播放的视图全屏达到目的。
1 |
|
但是很多的手机版本在网页视频播放时是不会调用这个方法的,所以这个方法局限性很大。
13、Android5.0上 WebView中Http和Https混合问题
1 | /** |
14、如何避免 WebView 的内存泄露问题
- 可以将 Webview 的 Activity 新起一个进程,结束的时候直接System.exit(0);退出当前进程;
- 不在xml中定义 WebView,而是在代码中创建,使用 getApplicationgContext() 作为传递的 Conetext;
- 在 Activity 销毁的时候,将 WebView 置空
1 |
|
总结
如果你踩到了 WebView 上的坑,请先默哀一分钟,然后努力找找解决方法吧,总会有人体验过你的悲剧,也会有人重蹈你的覆辙。
当然 WebView 里肯定不止我上面列出来的这些问题,如果你有更多的 WebView 问题解决方案欢迎评论交流。