webview攔截css,js,圖片後加載本地外部儲存的檔案(shouldOverrideUrlLoading)
阿新 • • 發佈:2019-02-15
說明:
Android WebView的快取機制就不多說了,這裡是單獨自己攔截css,js和圖片等自己進行快取。
需求:Android客戶端需要攔截網頁的每個css,js,圖片等,然後根據實際情況判斷來使用本地儲存卡或者assets中的js,css和圖片資源。
實現:
方式一:攔截後使用留儲存到外部儲存,然後使用流讀取外部儲存的檔案
原理:使用shouldOverrideUrlLoading方法單獨攔截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";