说明:
Android WebView的缓存机制就不多说了,这里是单独自己拦截css,js和图片等自己进行缓存。
需求:Android客户端需要拦截网页的每个css,js,图片等,然后根据实际情况判断来使用本地存储卡或者assets中的js,css和图片资源。
实现:
方式一:拦截后使用留存储到外部存储,然后使用流读取外部存储的文件
原理:使用shouldOverrideUrlLoading方法单独拦截css,js和图片。
参考:Android 拦截WebView加载URL,控制其加载CSS、JS资源
代码:
- webview设置
webView = new WebView(context);
WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true);//允许网页使用js
webView.setWebViewClient(new WebViewClient() {//拦截url
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {//拦截url中的资源文件(js,css等),进行缓存
if (url.contains("http://") || url.contains("https://")) {//url中包含save则存储
if (url.contains("saveout")) {//缓存数据到本地,下次取本地数据
LogUtils.d("缓存机制-->come in-->single or multi url:" + url);
//总存储文件夹
String path = context.getFilesDir() + "/baofu/sdkh5/cache";//得到存储
File pathFile = new File(path);
if (pathFile.exists()) {//BaoFu文件夹处理
String u = url.substring(0, url.indexOf("?"));//url作为存储文件的名字;
File uFile = new File(path + "/" + DataUtils.MD5(u));//因为不知道url多长,所以统一MD5加密一下长度不变
if (uFile.exists()) {//文件存在读取
LogUtils.d("缓存机制-->读取-->single or multi url:" + url);
return SaveDataUtils.getWebResource(uFile, url);
} else {//文件不存在存储成流
LogUtils.d("缓存机制-->存-->single or multi url:" + url);
SaveDataUtils.writeUrToStrealm(uFile, url);
}
} else {
pathFile.mkdirs();
}
} else if (url.contains("saveassets")) {//取sdk自带数据(下面方式二的assets存储)
String name = url.substring(url.lastIndexOf("/") + 1, url.indexOf("?"));
LogUtils.d("缓存机制-->come in-->preload url:" + url);
InputStream is = JSBase64Utils.getJSBase64Map(name);
if (is != null) {
return new WebResourceResponse("application/x-javascript", "UTF-8", is);
}
}
}
Log.i("shouldInterceptRequest", "load resource from internet, url: " + url);
return super.shouldInterceptRequest(view, url);//url中不包含save按照原来的url返回(网络数据)
}
});
webView.loadUrl(url);
- 上面用到的工具类
package mandao.component.utils;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.webkit.WebResourceResponse;
import android.widget.ImageView;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.security.MessageDigest;
import java.util.HashMap;
import java.util.Map;
/**
* js,css文件存储到本地
*/
public class SaveDataUtils {
/**
* 得到WebResourceResponse对象
* @return
*/
public static WebResourceResponse getWebResource(File uFile, String url) {
try {
URL uri = new URL(url);
URLConnection connection = uri.openConnection();
String contentType = connection.getContentType();
String mimeType = "";
String encoding = "";
if (contentType != null && !"".equals(contentType)) {
if (contentType.indexOf(";") != -1) {
String[] args = contentType.split(";");
mimeType = args[0];
String[] args2 = args[1].trim().split("=");
if (args.length == 2 && args2[0].trim().toLowerCase().equals("charset")) {
encoding = args2[1].trim();
} else {
encoding = "utf-8";
}
} else {
mimeType = contentType;
encoding = "utf-8";
}
}
FileInputStream fileInputStream = new FileInputStream(uFile);
SaveDataUtils.readBlock(fileInputStream);
SaveDataUtils.readBlock(fileInputStream);
return new WebResourceResponse(mimeType, encoding, fileInputStream);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 把js,css保存在本地
* @param uFile 本地存储的文件名File路径
* @param url 将要下载的js,css文件
*/
public static void writeUrToStrealm(File uFile, String url) {
try {
URL uri = new URL(url);
URLConnection connection = uri.openConnection();
InputStream uristream = connection.getInputStream();
//String cache = connection.getHeaderField("Ddbuild-Cache");
String contentType = connection.getContentType();
//textml; charset=utf-8
String mimeType = "";
String encoding = "";
if (contentType != null && !"".equals(contentType)) {
if (contentType.indexOf(";") != -1) {
String[] args = contentType.split(";");
mimeType = args[0];
String[] args2 = args[1].trim().split("=");
if (args.length == 2 && args2[0].trim().toLowerCase().equals("charset")) {
encoding = args2[1].trim();
} else {
encoding = "utf-8";
}
} else {
mimeType = contentType;
encoding = "utf-8";
}
}
//todo:缓存uristream
FileOutputStream output = new FileOutputStream(uFile);
int read_len;
byte[] buffer = new byte[1024];
SaveDataUtils.writeBlock(output, mimeType);
SaveDataUtils.writeBlock(output, encoding);
while ((read_len = uristream.read(buffer)) > 0) {
output.write(buffer, 0, read_len);
}
output.close();
uristream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 写入JS相关文件
* by黄海杰 at:2015-10-29 16:14:01
* @param output
* @param str
*/
public static void writeBlock(OutputStream output, String str) {
try {
byte[] buffer = str.getBytes("utf-8");
int len = buffer.length;
byte[] len_buffer = toByteArray(len, 4);
output.write(len_buffer);
output.write(buffer);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 读取JS相关文件
* @param input
* @return
*/
public static String readBlock(InputStream input) {
try {
byte[] len_buffer = new byte[4];
input.read(len_buffer);
int len = toInt(len_buffer);
ByteArrayOutputStream output = new ByteArrayOutputStream();
int read_len = 0;
byte[] buffer = new byte[len];
while ((read_len = input.read(buffer)) > 0) {
len -= read_len;
output.write(buffer, 0, read_len);
if (len <= 0) {
break;
}
}
buffer = output.toByteArray();
output.close();
return new String(buffer,"utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* int转byte
* by黄海杰 at:2015-10-29 16:15:06
* @param iSource
* @param iArrayLen
* @return
*/
public static byte[] toByteArray(int iSource, int iArrayLen) {
byte[] bLocalArr = new byte[iArrayLen];
for (int i = 0; (i < 4) && (i < iArrayLen); i++) {
bLocalArr[i] = (byte) (iSource >> 8 * i & 0xFF);
}
return bLocalArr;
}
/**
* byte转int
* by黄海杰 at:2015-10-29 16:14:37
* @param bRefArr
* @return
*/
// 将byte数组bRefArr转为一个整数,字节数组的低位是整型的低字节位
public static int toInt(byte[] bRefArr) {
int iOutcome = 0;
byte bLoop;
for (int i = 0; i < bRefArr.length; i++) {
bLoop = bRefArr[i];
iOutcome += (bLoop & 0xFF) << (8 * i);
}
return iOutcome;
}
}
方式二:拦截后使用assets中的js、css和图片等资源
提前把css、js、图片等资源放在assets下面,打包到apk包中,程序运行可以直接调用assets目录下面的文件。
WebView webView = new WebView(this);
webView.setWebViewClient(new WebViewClient() {
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
Log.i(LOGTAG, "shouldInterceptRequest url=" + url + ";threadInfo" + Thread.currentThread());
WebResourceResponse response = null;
if (url.contains("logo")) {
try {
InputStream localCopy = getAssets().open("droidyue.png");
response = new WebResourceResponse("image/png", "UTF-8", localCopy);
} catch (IOException e) {
e.printStackTrace();
}
}
return response;
}
});
setContentView(webView);
webView.loadUrl("http://m.sogou.com");
注意:
1、shouldInterceptRequest有两种重载。
• public WebResourceResponse shouldInterceptRequest (WebView view, String url) 从API 11开始引入,API 21弃用
• public WebResourceResponse shouldInterceptRequest (WebView view, WebResourceRequest request) 从API 21开始引入
本次例子暂时使用第一种,即shouldInterceptRequest (WebView view, String url)
2、return注意
shouldOverrideUrlLoading 的返回值使用的是 FileInputStream
assets 的返回值 使用的是 InputStream
其中:三个属性 --> MIME类型,数据编码,数据(InputStream流形式)。
3、权限注意
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
4、mimeType
new WebResourceResponse("image/png", "UTF-8", localCopy); 第一个参数对应的如下:
js: mimeType = "application/x-javascript";
css: mimeType = "text/css";
html: mimeType = "text/html";
jpg/png: mimeType = "image/png";