环境:基于Android API-30
WebView适用的内核是webkit,在4.4版本之后,直接使用Chrome作为内置网页浏览器。 使用的时候需要在清单文件加网络权限(如果有读写则添加读写权限):
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
从API 28开始,默认情况下禁用明文支持,http的url均无法再webview中加载,解决办法:在清单文件中application节点添加:
android:usesCleartextTraffic="true"
注意:
// 5.1以上默认禁止了https和http混用,可以如下开启:
webView.settings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
一般使用:
webView.settings.javaScriptEnabled = true
webView.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(
view: WebView?,
request: WebResourceRequest?
): Boolean {
if (request != null) {
view?.loadUrl(request.url.toString())
}
return true
}
}
webView.loadUrl("http://www.baidu.com")
如果访问的网页中有Javascript,则必须设置支持Javascript
webView.getSettings().setJavaScriptEnabled(true);
如果希望点击webView中的链接在当前browser响应,而不是打开Android系统browser中响应该连接,则需要覆盖WebView的WebViewClient:
webView.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(
view: WebView?,
request: WebResourceRequest?
): Boolean {
if (request != null) {
view?.loadUrl(request.url.toString())
}
return true
}
}
加载h5方式:
// 加载网络h5
webView.loadUrl("http://www.baidu.com")
// 加载assets文件下的h5
webView.loadUrl("file:///android_asset/test.html")
// 加载sdcard的h5,需要读写权限
webView.loadUrl("file:///xxx/test.html")
// 加载html代码段
val str = "<html><body>Hello <b>world</b></body></html>"
webView.loadData(str,"text/html",null)
前进/后退 返回栈
//是否可以后退
webView.canGoBack()
//后退网页
webView.goBack()
//是否可以前进
webView.canGoForward()
//前进网页
webView.goForward()
//以当前的index为起始点前进或者后退到历史记录中指定的steps
//如果steps为负数则为后退,正数则为前进
val step = 1
webView.goBackOrForward(step)
清除缓存数据
//清除网页访问留下的缓存
//由于内核缓存是全局的因此这个方法不仅仅针对webview而是针对整个应用程序.
webView.clearCache(true)
//清除当前webview访问的历史记录
//只会清除访问历史记录里的所有记录(除了当前访问记录)
webView.clearHistory()
//这个api仅仅清除自动完成填充的表单数据,并不会清除WebView存储到本地的数据
webView.clearFormData()
WebSettings类
val webSettings = webView.settings
//如果访问的页面中要与Javascript交互,则webview必须设置支持Javascript
// 若加载的 html 里有JS 在执行动画等操作,会造成资源浪费(CPU、电量)
// 在 onStop 和 onResume 里分别把 setJavaScriptEnabled() 给设置成 false 和 true 即可
webSettings.javaScriptEnabled = true
// 支持插件:已废弃,SystemApi,无法直接调用
// webSettings.setPluginsEnabled(true)
//设置自适应屏幕,两者合用
webSettings.useWideViewPort = true //将图片调整到适合webview的大小
webSettings.loadWithOverviewMode = true // 缩放至屏幕的大小
//缩放操作
webSettings.setSupportZoom(true) //支持缩放,默认为true。是下面那个的前提。
webSettings.builtInZoomControls = true //设置内置的缩放控件。若为false,则该WebView不可缩放
webSettings.displayZoomControls = false //隐藏原生的缩放控件
//其他细节操作
webSettings.cacheMode = WebSettings.LOAD_CACHE_ELSE_NETWORK //关闭webview中缓存
webSettings.allowFileAccess = true //设置可以访问文件
webSettings.javaScriptCanOpenWindowsAutomatically = true //支持通过JS打开新窗口
webSettings.loadsImagesAutomatically = true //支持自动加载图片
webSettings.defaultTextEncodingName = "utf-8" //设置编码格式
设置WebView缓存
- 当加载html页面时,WebView会在/data/data/包名目录下生成database与cache两个文件夹
- 请求的URL记录保存在WebViewCache.db,而URL的内容是保存在WebViewCache文件下
是否使用缓存:
//优先使用缓存:
webSettings.cacheMode = WebSettings.LOAD_CACHE_ELSE_NETWORK
//缓存模式如下:
//LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据
//LOAD_DEFAULT: (默认)根据cache-control决定是否从网络上取数据。
//LOAD_NO_CACHE: 不使用缓存,只从网络获取数据.
//LOAD_CACHE_ELSE_NETWORK,只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据。
//不使用缓存:
webSettings.cacheMode = WebSettings.LOAD_NO_CACHE
离线加载:
if (isConnected(applicationContext)) {
webSettings.cacheMode = WebSettings.LOAD_DEFAULT //根据cache-control决定是否从网络上取数据。
} else {
webSettings.cacheMode = WebSettings.LOAD_CACHE_ELSE_NETWORK //没网,则从本地获取,即离线加载
}
webSettings.domStorageEnabled = true // 开启 DOM storage API 功能
webSettings.databaseEnabled = true //开启 database storage API 功能
webSettings.setAppCacheEnabled(true) //开启 Application Caches 功能;已经废弃
val cacheDirPath = filesDir.absolutePath + APP_CACAHE_DIRNAME
webSettings.setAppCachePath(cacheDirPath) //设置 Application Caches 缓存目录;已经废弃
注意:每个Application只调用一次webSettings.setAppCachePath,WebSettings.setAppCacheMaxSize()
WebViewClient类
shouldOverrideUrlLoading是在本地显示还是在系统浏览器显示;可以拦截url进行处理。
webView.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(
view: WebView?,
request: WebResourceRequest?
): Boolean {
if (request != null) {
view?.loadUrl(request.url.toString())
}
return true
}
}
开始载入页面的回调,可以设定loading显示,等待网络响应。
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
// 加载开始的操作
}
加载结束回调。可以关闭loading。
override fun onPageFinished(view: WebView?, url: String?) {
// 加载结束的操作
}
加载资源回调,每一个资源(如:图片)的加载都会调用一次
override fun onLoadResource(view: WebView?, url: String?) {
// 加载资源
}
加载页面时出现错误(如:404),我们可以加载一个本地的错误页面替代系统丑陋的页面。(再例如在无网络的时候error会报:net::ERR_NAME_NOT_RESOLVED),可以根据错误来显示具体信息。但是注意:有可能多次错误回调。
override fun onReceivedError(
view: WebView?,
request: WebResourceRequest?,
error: WebResourceError?
) {
super.onReceivedError(view, request, error)
}
处理https请求,webView默认是不处理https请求的,页面显示空白等问题。
override fun onReceivedSslError(
view: WebView?,
handler: SslErrorHandler?,
error: SslError?
) {
if (handler != null) {
handler.proceed()// 等待证书响应
handler.cancel()// 挂起连接,默认
handler.handleMessage(handler.obtainMessage())// 做其他处理
}
}
WebChromeClient类
获得网页加载进度并显示:
override fun onProgressChanged(view: WebView?, newProgress: Int) {
}
获取Web页中的标题
override fun onReceivedTitle(view: WebView?, title: String?) {
}
javaScript警告,拦截js alert转换原生dialog
override fun onJsAlert(
view: WebView?,
url: String?,
message: String?,
result: JsResult?
): Boolean {
return true
}
javaScript确认框
override fun onJsConfirm(
view: WebView?,
url: String?,
message: String?,
result: JsResult?
): Boolean {
// 返回ture表示点击确认;返回false表示点击取消
return super.onJsConfirm(view, url, message, result)
}
javaScript输入框,点击确认返回输入框中的值,点击取消返回null
override fun onJsPrompt(
view: WebView?,
url: String?,
message: String?,
defaultValue: String?,
result: JsPromptResult?
): Boolean {
return super.onJsPrompt(view, url, message, defaultValue, result)
}
避免WebView内存泄漏
不要在xml定义WebView,而是在需要的时候在Activity中创建,并且Context使用getApplicationContext()
val param = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT)
var myWebView = WebView(applicationContext)
myWebView.layoutParams = param
rootView.addView(webView)
在activity销毁WebView的时候,先WebView加载null内容,然后移除WebView再销毁WebView,然后置null
override fun onDestroy() {
if (webView != null) {
webView.loadDataWithBaseURL(null, "", "text/html", "utf-8", null)
webView.clearHistory()
(webView.parent as ViewGroup).removeView(webView)
webView.destroy()
}
super.onDestroy()
}
常见问题:
存在内存泄漏点:
- WVJWebViewClientProxy泄漏
- WVJBWebView下的messageHandlers泄漏 解决:
// 在WVJBWebView类中添加解注册
public void unregisterAllHandler() {
messageHandlers.clear();
}
// 在销毁webView的时候(例如activity的onDestroy需要调用)
webView!!.unregisterAllHandler()
webView!!.clientProxy = null
存在数据解析异常:特别是h5和native交互的时候特殊字符,例如:h5传消息到native,"""" json中双引号嵌套,虽然不crash,但是消息无法解析传递。 建议:不要在项目里再用这个库了