Android - webview拦截css,js,图片后加载本地外部存储的文件(shouldOverrideUrlLoading)

公良理
2023-12-01

说明:

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

 

 

 类似资料: