Webview自定义缓存网页加载的图片资源

因为公司与客户公司合作越来越多,公司的手机应用项目类型也从纯原生应用变成了原生+Web混编的方式,使得项目周期大幅缩减,但随之而来的也产生了新的问题:网页载入速度缓慢的情况下如何提高用户体验度。因此就需要使用缓存机制。然而公司并不是要使用Webview本身自带的缓存机制,而是有针对性的进行缓存,比如只将图片缓存,再次载入页面就使用本地缓存的图片代替网页图片,以提高网页的载入速度。

基于以上需求,需要在webview初始化的时候重写WebViewClient的shouldInterceptRequest方法。
源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
WebResourceResponse response = null;
response = cacheImages(url);
return response;
}

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, final WebResourceRequest request) {
Uri uri = request.getUrl();
WebResourceResponse response = null;
if (uri != null) {
final String url = uri.toString();
response = cacheImages(url);
}
//如果response为null,webview会自动获取网络图片
return response;
}

因为在API21的时候谷歌废弃了shouldInterceptRequest(WebView view, String url)这个方法,因此在写的时候需要重写两个方法,并添加API的注解@TargetApi(Build.VERSION_CODES.HONEYCOMB)@TargetApi(Build.VERSION_CODES.LOLLIPOP)对不同的API版本的系统进行区分。
而对应的在载入页面的时候所调用的方法也一起放在这里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
private void saveImages() {
SaveImage saveImage = new SaveImage();
saveImage.execute();
}

private boolean cachedImageIsExist(String path) {
File file = new File(path);
// 判断文件存在且大小不能为0
if (file.exists() && file.length() > 0) {
return true;
} else {
return false;
}
}

private File getDirPath() {
File file = new File(MConstants.CACHEPATH);
if (!file.exists()) {
file.mkdirs();
}
return file;
}

private WebResourceResponse cacheImages(String url) {
WebResourceResponse response = null;
// 如果本地没有则缓存到本地
if (url.contains("image.do")) {
String imageName = url.substring(url.lastIndexOf("/") + 1, url.length()) + ".png";
if (cachedImageIsExist(MConstants.CACHEPATH + imageName)) {
InputStream cachedImage = null;
try {
cachedImage = new FileInputStream(MConstants.CACHEPATH + imageName);
} catch (FileNotFoundException e) {
KLog.e(e.getMessage());
}
response = new WebResourceResponse("image/png", "UTF-8", cachedImage);
} else {
imageUrls.add(url);
}
}
return response;
}

/***
* 功能:用线程保存图片
*
* @author wangyp
*/
private class SaveImage extends AsyncTask<String, Void, String> {
@Override
protected String doInBackground(String... params) {
String result = "";
try {
File file = getDirPath();
// 从最后一个"/"截取到最末尾,并加上 ".png"后缀
for (String imgUrl : imageUrls) {
String ext = imgUrl.substring(imgUrl.lastIndexOf("/") + 1, imgUrl.length()) + ".png";
file = new File(MConstants.CACHEPATH + ext);
URL url = new URL(imgUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
KLog.e(conn.getHeaderField("Content-Length"));
if (!file.exists() || !conn.getHeaderField("Content-Length").equals(String.valueOf(file.length()))) {
// 文件不存在或大小不一致则下载图片
InputStream inputStream = null;
conn.setRequestMethod("GET");
conn.setConnectTimeout(20000);
if (conn.getResponseCode() == 200) {
inputStream = conn.getInputStream();
}
byte[] buffer = new byte[4096];
int len = 0;
FileOutputStream outStream = new FileOutputStream(file);
while ((len = inputStream.read(buffer)) != -1) {
outStream.write(buffer, 0, len);
}
outStream.close();
}
}
result = "图片已保存至:" + file.getAbsolutePath();
} catch (Exception e) {
result = "保存失败!" + e.getLocalizedMessage();
}
return result;
}

@Override
protected void onPostExecute(String result) {
KLog.e(result);
}
}

从上面的代码可以看出来,我们是用“image.do”这个Url中存在的字符串进行区分是否需要缓存。当包含了“image.do”的时候则进行缓存的判断,本地存在同名的png图片文件,则替换页面需要载入的网络资源为本地已缓存图片,如果不存在,则将url添加到需要缓存的图片的地址列表里。最后在onPageFinished方法中启动异步线程任务下载图片。而在下载的时候对文件是否已存在进行判断,如果存在则跳过,如果不存在则下载,这样就可以节省一些流量,当然了,文件是否损坏的判断方式,我采用的是直接判断文件的大小,这个大小需要网页端将文件大小存放在http头部信息中的“Content-Length”字段里面。
这仅仅是一个简单的思路和方法,有问题的话欢迎留言告诉我。
==转载请注明原文出处,谢谢。==