Webview缓存剖析以及解决方案

1 Android WebView 存在什么性能问题?

H5 页面加载速度慢

  1. 渲染速度慢
  2. 页面资源加载缓慢

耗费流量
每次使用 H5页面时,用户都需要重新加载 Android WebView的H5 页面
每加载一个 H5页面,都会产生较多网络请求
每一个请求都串行的,这么多请求串起来,这导致消耗的流量也会越多

2 解决方案

本文从3个角度入手
1.WebView自带机制
2.资源预加载
3.资源拦截

WebView自带(前端H5的缓存机制)

这里的缓存是什么?
缓存即离线储存,在H5网页加载后会储存在缓存区域,在无网络连接时也可以访问

android WebView的本质
在android中嵌入H5就是WebView本质,WebView自带的缓存机制其实就是H5页面的缓存机制
目前WebView暂时不支持File System缓存机制。

缓存机制:如何将加载过得网页数据保存到本地
缓存模式:加载网页时如何读取之前保存到本地的网页缓存。

3 五种缓存机制与缓存模式

3.1 缓存机制

  • 浏览器 缓存机制
  • Application Cache 缓存机制
  • Dom Storage 缓存机制
  • Web SQL Database 缓存机制
  • Indexed Database 缓存机制
  1. 浏览器缓存机制(标准实现)
    特点:
    支持Http协议层,缓存文件需要首次加载后才产生,浏览器缓存的存储空间有限,缓存有被清除的可能,缓存文件没有校验,无需做操作,浏览器内核机制,一般都是标准实现。
    应用场景:
    静态资源文件的存储,如JS、CSS、字体、图片等。

  2. Application Cache 缓存机制
    特点:
    方便构建Web App的缓存,专门为Web App离线使用二开发的缓存机制,
    应用场景:
    同浏览器缓存机制,但AppCache 是对 浏览器缓存机制 的补充,不是替代。

        WebSettings mWebSettings = getSettings();
        String cachePath = getContext().getFilesDir().getAbsolutePath()+"cache/";
        mWebSettings.setAppCachePath(cachePath);  // 1. 设置缓存路径
        mWebSettings.setAppCacheMaxSize(20*1024*1024);  // 2. 设置缓存大小
        mWebSettings.setAppCacheEnabled(true); // 3. 开启Application Cache存储机制
  1. Dom Storage 缓存机制
    特点:
    通过存储字符串的K-V对来提供,存储空间较大(HTML5 的建议是每个网站提供给 Storage 的空间是 5MB),存储安全便捷。Dom Storage 存储的数据在本地,不需要经常和服务器进行交互
    应用场景:
    存储临时、简单的数据
    代替 将 不需要让服务器知道的信息 存储到 cookies 的这种传统方法
    Dom Storage 机制类似于 Android 的 SharedPreference机制
 settings.setDomStorageEnabled(true);// 开启DOM storage
  1. Web SQL Database 缓存机制
    特点:基于SQL Database的缓存机制,充分利用数据库的优势,可方便对数据库进行增删改查
    应用场景
    存储适合数据库的结构化数据
 // 通过设置WebView的settings实现
        WebSettings settings = getSettings();
        String cacheDirPath = context.getFilesDir().getAbsolutePath()+"cache/";
        settings.setDatabasePath(cacheDirPath);// 设置缓存路径
        settings.setDatabaseEnabled(true); // 开启 数据库存储机制

特别说明
根据官方说明,Web SQL Database存储机制不再推荐使用(不再维护)
取而代之的是 IndexedDB缓存机制

  1. IndexedDB 缓存机制
    特点
    属于NoSQL数据库,通过存储字符串的K-V对来提供,存储空间大,使用灵活
    应用场景
    存储复杂、数据量大的结构化数据。
    具体实现
        // 只需设置支持JS就自动打开IndexedDB存储机制
        // Android 在4.4开始加入对 IndexedDB 的支持,只需打开允许 JS 执行的开关。
    settings.setJavaScriptEnabled(true);
Webview缓存剖析以及解决方案_第1张图片

3.2 缓存模式

缓存模式是一种 当加载 H5网页时 该如何读取之前保存到本地缓存从而进行使用的方式

  • Android WebView 自带的缓存模式有4种:
  1. LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据
  2. LOAD_NO_CACHE: 不使用缓存,只从网络获取数据.
  3. LOAD_DEFAULT: (默认)根据cache-control决定是否从网络上取数据。
  4. LOAD_CACHE_ELSE_NETWORK,只要本地有,无论是否过期,或者no-cache,都使用
//WebView.getSettings().setCacheMode(LOAD_XXX);
        if (NetworkUtils.isConnected()) {
            mWebSettings.setCacheMode(WebSettings.LOAD_DEFAULT);//根据cache-control决定是否从网络上取数据。
        } else {
            mWebSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);//没网,则从本地获取,即离线加载
        }

4 资源预加载

  1. 预加载WebView对象
  2. 预加载H5资源

5 自身构建缓存

5.1 实现步骤

  1. 事先将更新频率较低、常用 & 固定的H5静态资源 文件(如JS、CSS文件、图片等) 放到本地
  2. 拦截H5页面的资源网络请求 并进行检测
  3. 如果检测到本地具有相同的静态资源 就 直接从本地读取进行替换而不发送该资源的网络请求 到 服务器获取

5.2 具体实现

        mWebview.setWebViewClient(new WebViewClient() {
            // 重写 WebViewClient  的  shouldInterceptRequest ()
            // API 21 以下用shouldInterceptRequest(WebView view, String url)
            // API 21 以上用shouldInterceptRequest(WebView view, WebResourceRequest request)
            @Override
            public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
                // 步骤1:判断拦截资源的条件,即判断url里的图片资源的文件名
                // 假设网页里该图片资源的地址为:http://abc.com/imgage/logo.gif
                // 图片的资源文件名为:logo.gif
                if (url.contains("logo.gif")) {
                    InputStream is = null;         // 步骤2:创建一个输入流
                    try {
                        // 步骤3:获得需要替换的资源(存放在assets文件夹里)
                        is =getContext().getApplicationContext().getAssets().open("images/abc.png");
                        // a. 先在app/src/main下创建一个assets文件夹
                        // b. 在assets文件夹里再创建一个images文件夹
                        // c. 在images文件夹放上需要替换的资源(此处替换的是abc.png图片)
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    // 步骤4:替换资源
                    WebResourceResponse response = new WebResourceResponse("image/png",
                            "utf-8", is);
                    // 参数1:http请求里该图片的Content-Type,此处图片为image/png
                    // 参数2:编码类型
                    // 参数3:存放着替换资源的输入流(上面创建的那个)
                    return response;
                }
                return super.shouldInterceptRequest(view, url);
            }
            // API 21 以上用shouldInterceptRequest(WebView view, WebResourceRequest request)
            @TargetApi(Build.VERSION_CODES.LOLLIPOP)
            @Override
            public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
                // 步骤1:判断拦截资源的条件,即判断url里的图片资源的文件名
                if (request.getUrl().toString().contains("logo.gif")) {
                    // 假设网页里该图片资源的地址为:http://abc.com/imgage/logo.gif
                    // 图片的资源文件名为:logo.gif
                    InputStream is = null;    // 步骤2:创建一个输入流
                    try {
                        // 步骤3:获得需要替换的资源(存放在assets文件夹里)
                        is = getContext().getApplicationContext().getAssets().open("images/abc.png");
                        // a. 先在app/src/main下创建一个assets文件夹
                        // b. 在assets文件夹里再创建一个images文件夹
                        // c. 在images文件夹放上需要替换的资源(此处替换的是abc.png图片

                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    // 步骤4:替换资源
                    WebResourceResponse response = new WebResourceResponse("image/png",
                            "utf-8", is);
                    // 参数1:http请求里该图片的Content-Type,此处图片为image/png
                    // 参数2:编码类型
                    // 参数3:存放着替换资源的输入流(上面创建的那个)
                    return response;
                }
                return super.shouldInterceptRequest(view, request);
            }
        });

你可能感兴趣的:(Webview缓存剖析以及解决方案)