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";