Rexxar 有很多拦截资源请求操作,刚看的时候乱的很,这里是结合缓存来实现的,下一节会介绍缓存机制,这里我们就需要知道返回给 WebView 的 WebResourceResponse 数据都是从缓存中读取即可。
所有的代码都在 RexxarWebViewClient 类当中,只需要看 handleResourceRequest(WebView webView, String requestUrl) 这一个方法。
具体过程
Rexxar 拦截 html,js 资源直接渲染进程返回,图片等其他资源先返回空的数据流再异步向流中写数据,因为渲染线程做太多工作会耗时并且 block 页面渲染,造成不好的显示效果。
我这里只介绍关于Html部分,js 同理。
首先需要判断当前url是否需要拦截处理,不需要则走系统方法,让webview自行处理,满足条件才拦截处理,这里 url 需要满足 非null 非本地 host 是豆瓣相关域名,详细就不多说了, 可以看代码:
/**
* @param requestUrl
* @return
*/
private boolean shouldIntercept(String requestUrl) {
if (TextUtils.isEmpty(requestUrl)) {
return false;
}
// file协议需要替换,用于html
if (requestUrl.startsWith(Constants.FILE_AUTHORITY)) {
return true;
}
// rexxar container api,需要拦截
if (requestUrl.startsWith(Constants.CONTAINER_API_BASE)) {
return true;
}
// 非合法uri,不拦截
Uri uri = null;
try {
uri = Uri.parse(requestUrl);
} catch (Exception e) {
e.printStackTrace();
}
if (null == uri) {
return false;
}
// 非合法host,不拦截
String host = uri.getHost();
if (TextUtils.isEmpty(host)) {
return false;
}
// 不能拦截的uri,不拦截
Pattern pattern;
Matcher matcher;
for (String interceptHostItem : ResourceProxy.getInstance().getProxyHosts()) {
pattern = Pattern.compile(interceptHostItem);
matcher = pattern.matcher(host);
if (matcher.find()) {
return true;
}
}
return false;
}
WebResourceResponse
WebResourceResponse 是关键的返回对象,会构造一个正确类型的输入流返回给 WebView ,不为 null WebView 就不会自己去下载了,也就节省了 WebView 访问网络资源。
图片异步返回
这部分是过滤拦截中比较重要的部分,代码较为复杂凌乱,主要是有很多错误情况处理,应为并不是每次数据都是合理和正确的。
这里采用的方案是:先返回一个空的 InputStream,然后再通过异步的方式向里面写数据。
String fileExtension = MimeTypeMap.getFileExtensionFromUrl(requestUrl);
String mimeType = MimeUtils.guessMimeTypeFromExtension(fileExtension);
首先要获取 url 的扩展名,也就是是什么类型的文件,然后根据类型获取 MimeType,在创建 WebResourceResponse 的时候传入,才是合理的类型。
WebResourceResponse xResponse = new WebResourceResponse(mimeType, "UTF-8", in);
然后在渲染线程中 post 一个 Runnable 异步的返回数据,当然这里做了 try catch 处理,如果期间出现异常,直接调用 super.shouldInterceptRequest(webView, requestUrl) 方法,让系统处理,从而不影响原生功能使用。
因为在返回空的 InputStream 时,我们已经创建了 mOut 对象,所以在 ResourceRequest 线程中,需要在获取到数据时通过 mOut 写如数据。
例如:
byte[] bytes = IOUtils.toByteArray(cacheEntry.inputStream);
LogUtils.i(TAG, "load async cache hit :" + mUrl);
mOut.write(bytes);
这是一次正常的读取缓存成功操作并返回。
还有使用 native 访问网络处理的情况,相对缓存就是多了一步下载图片或者文件的操作,并且成功也要缓存到本地。同样需要对数据进行严格校验,出现任何失败都要分会相应的错误状态。
具体错误类型就不介绍了,这个可能和业务有关。
RexxarContainerAPI
这里简单介绍下 RexxarContainerAPI,和 RexxarWidget 类似,可以给 Web 一个 Native 的计算结果。RexxarContainerAPI 可以有很多的实现类,每一种都代表一种 Api 请求类型,用来处理不同的业务,只需要在不用业务页面添加所需要的 Api 功能即可,提供了一种通用的处理机制。
例如:
/ / 设置local api
mRexxarWebView.addContainerApi(new FrodoContainerAPIs.LocationAPI());
mRexxarWebView.addContainerApi(new FrodoContainerAPIs.LogAPI());
这里引用下官方介绍:
我们为 iOS 和 Android 各开发了一个 Rexxar Container。iOS 和 Android 平台截获请求的方式由于平台差异,并不完全相同。但本质上都是在 Web 和 Native 之间实现了一个 Proxy。Web 发出的请求会被 Proxy 预先处理。要么是修改后再发出去,要么是由 Rexxar Container 自己处理。
Rexxar Android 系列学习其他文章
Rexxar Android 系列学习(1) 项目结构
Rexxar Android 系列学习(2) 路由协议
Rexxar Android 系列学习(3) Native 和 web 交互
Rexxar Android 系列学习(4) 错误处理
Rexxar Android 系列学习(5) 过滤拦截
Rexxar Android 系列学习(6) 缓存机制