携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第4天,点击查看活动详情。
一、简介
按照惯例,象征性的给大家贴上官方WebView文档地址,不知道有没有同学发现,android.jar中WebView extends MockView
,
而系统WebView的源码里面看到WebView extends AbsoluteLayout
我们发现集成自MockView的WebView只是暴露了接口,没有具体实现。 android.jar中的WebView是一个模拟版本,仅用于开发目的,使用模拟版本webview可以在布局窗口中呈现Webview,可以在代码中调用API。在运行时, 将替换WebView。
二、基本使用
1、网络权限不可少
<uses-permission android:name="android.permission.INTERNET"/>
2、其他常用的方法:
- setJavaScriptEnabled(boolean flag): 是否支持JS使用。
- setDefaultTextEncodingName(String encoding): 设置编码。
- setCacheMode(int mode): 设置WebView的缓存模式。
- setSupportZoom(boolean support): 是否支持缩放。
- setAppCacheEnabled(boolean flag): 是否启用缓存模式。
- setDomStorageEnabled(boolean flag): 是否开启DOM缓存。
- setDatabaseEnabled(boolean flag): 是否开启数据库缓存
- getLoadsImagesAutomatically(): 是否支持自动加载图片。
- setLoadsImagesAutomatically(boolean flag): 支持自动加载图片
- setAllowFileAccess(boolean allow): 是否允许加载本地Html文件。
- WebView.enableSlowWholeDocumentDraw(): 智能的选择HTML文档中需要绘制的部分来减少内存占用并提高性能。
3、WebViewClient:回调对应的方法改变网页内容的呈现方式
/**
* 在开始加载网页时会回调
*/
public void onPageStarted(WebView view, String url, Bitmap favicon)
/**
* 在结束加载网页时会回调
*/
public void onPageFinished(WebView view, String url)
/**
* 拦截 url 跳转,在里边添加点击链接跳转或者操作
*/
public boolean shouldOverrideUrlLoading(WebView view, String url)
/**
* 加载错误的时候会回调,在这里可做错误处理;比如再请求加载一次;或者提示404的错误页面
*/
public void onReceivedError(WebView view, int errorCode,String description, String failingUrl)
/**
* 当接收到https错误时,会回调此函数;这里可以做错误处理
*/
public void onReceivedSslError(WebView view, SslErrorHandler handler,SslError error)
/**
* 在每一次请求资源时,都会通过这个函数来回调
*/
public WebResourceResponse shouldInterceptRequest(WebView view,
String url) {
return null;
}
三、如何加载本地图片?
我们在上面介绍到了WebViewClient里面的shouldInterceptRequest方法,每一次请求资源的时候,都会通过这个函数回调。
如果这个时候,我们的WebView接收到从相册或者相机选择完的图片回来,并设置到了Html里面,Markdown触发渲染,当回调到shouldInterceptRequest方法的时候,我们可以在这里面加载图片资源,那么怎么加载呢?往下看👇
shouldInterceptRequest 可以拿到当前请求资源的url
,我们需要先判断这个url
是不是我们Android手机图片地址的uri,首行需要增加如下代码:
// WebViewClient#shouldInterceptRequest
if(!url.startsWith("file://") && !url.startsWith("content://")){
return null
}
匹配成功,我们打印出来,可能我们的uri里面含%2F
,这个是URLEncoder.encode自带的转义符,我们可以用URLDecoder.decode返回去除转义后的内容,当然,也可以直接用下面这个代码,也是可行的:
val urlString = url.replace("%2F","/")
我们这里再浅谈一下URLDecoder 和 URLEncoder:
- URLDecoder类包含一个decode(String s,String enc)静态方法,它可以将application/x-www-form-urlencoded MIME字符串转成普通字符串;
- URLEncoder类包含一个encode(String s,String enc)静态方法,它可以将普通字符串转换成application/x-www-form-urlencoded MIME字符串。
为了解决乱码,我们会设置enc = "UTF-8"
如果我们和Web前端开发套壳联调H5的时候,如果出现WebView调用vue.js方法出现scrpit.error
这种的,基本上就是没有做好转义处理,切记切记。
从上面看完,我们拿到了文件的Uri,那我们可以看到shouldInterceptRequest方法返回的是WebResourceResponse,这个对象是干啥的?
WebResourceResponse:使用指定的MIME类型、字符编码和输入流,构造资源响应,用来加载本地缓存资源。
既然是手机本地的图片文件,我们可以使用ContentResolver#openFileDescriptor的方法,返回一个指向文件的新ParcelFileDescriptor,我们在WebResourceResponse构造方法中需要传一个InputStream,我们可以使用文件描述符,创建一个FileInputStream,我们看到FileInputStream有下面这个构造方法:
public FileInputStream(FileDescriptor fdObj)
有了上面的准备,我们接下来,需要区分一下,file scheme和content scheme,就可以返回WebResourceResponse啦。
1.File Scheme需要做如下处理:
// file scheme
if (filePath.startsWith("file://")) {
// 去掉file://前缀创建File对象
val file = File(filePath.substring("file://".length))
// 通过file.extension得到mimeType
val mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(file.extension)
val parcelFileDescriptor: ParcelFileDescriptor?
try {
parcelFileDescriptor = context.contentResolver.openFileDescriptor(Uri.parse(filePath), "r")
} catch (e:Exception){
return null
}
if(null == parcelFileDescriptor){
return null
}
// 返回本地的内容的Response
return WebResourceResponse(mimeType, StandardCharsets.UTF_8.name(), FileInputStream(parcelFileDescriptor.fileDescriptor))
}
2.Content Scheme需要做如下处理:
if (filePath.startsWith("content://")) {
// 获取FileUri
val fileUri = Uri.parse(filePath)
// 获取mimeType
val mimeType = context.contentResolver.getType(fileUri)
val parcelFileDescriptor: ParcelFileDescriptor?
try {
parcelFileDescriptor = context.contentResolver.openFileDescriptor(fileUri, "r")
}catch (e:Exception){
return null
}
if(null == parcelFileDescriptor){
return null
}
// 返回本地的内容Response
return WebResourceResponse(mimeType, StandardCharsets.UTF_8.name(), FileInputStream(parcelFileDescriptor.fileDescriptor))
}
成功返回WebResourceResponse,就可以正常渲染本地资源了!