1. 程式人生 > >webview攔截css,js,圖片後加載本地外部儲存的檔案(shouldOverrideUrlLoading)

webview攔截css,js,圖片後加載本地外部儲存的檔案(shouldOverrideUrlLoading)

說明:

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