WebView之性能优化,优化加载速度,缓存机制详解,解压服务器!

首先先声明一下工作中遇到的问题,我们项目在晚上九点的时候,是一个高峰期。由于服务器高并发没有优化好,在短时间内要做一下解决方案。

还有一个是优化加载webview的加载速度。这个放在后面说。

场景,晚上九点,是用户活跃高峰期,导致原生App端,打开H5页面,需要请的CSS和JS这些文件特别的慢,所有H5页面会在App上显示布局错乱,显示有问题等情况!以下做出了几种方案。(当然后台解决高并发是核心关键

第一:把H5打包成zip文件上传服务器。然后App启动的时候设置一个进度条下载到本地,然后解压。打开本地的HTML,CSS这样,图像,JS文件就全部在本地了,就不会出现高峰期再去请求服务器页面显示错误了。更新只需要删除文件重新下载即可

第二:。使用缓存策略解决这个问题从缓存读取CSS和静态文件更新只需要清空缓 即可。 先介绍一下缓存 当我们加载的Html时候,会在我们的数据/应用包下生成数据库与缓存两个文件夹: 我们请求的地址记录是保存在webviewCache.db里,而URL的内容是保存在webviewCache文件夹下。

当我们加载的Html时候,会在我们的数据/应用包下生成数据库与缓存两个文件夹:

我们请求的Url记录是保存在webviewCache.db里,而url的内容是保存在webviewCache文件夹下.
WebView中存在着两种缓存:网页数据缓存(存储打开过的页面及资源)、H5缓存(即AppCache)

我们请求的Url记录是保存在webviewCache.db里,而url的内容是保存在webviewCache文件夹下.
WebView中存在着两种缓存:网页数据缓存(存储打开过的页面及资源)、H5缓存(即AppCache)

一、网页缓存

1、缓存构成

/data/data/package_name/cache/
/data/data/package_name/database/webview.db
/data/data/package_name/database/webviewCache.db

LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据
LOAD_DEFAULT: 根据cache-control决定是否从网络上取数据。
LOAD_CACHE_NORMAL: API level 17中已经废弃, 从API level 11开始作用同LOAD_DEFAULT模式
LOAD_NO_CACHE: 不使用缓存,只从网络获取数据.
LOAD_CACHE_ELSE_NETWORK,只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据。

如:www.taobao.com的缓存控制为无缓存,在模式LOAD_DEFAULT下,无论如何都会从网络上取数据,如果没有网络,就会出现错误页面;在LOAD_CACHE_ELSE_NETWORK模式下,无论是否有网络,只要本地有缓存,都使用缓存。本地没有缓存时才从网络上获取。

www.360.com.cn的cache-control为max-age = 60,LOAD_DEFAULT模式下缓存60秒,意思就是60后访问才能重新请求服务器。但是,这60秒后发生了数据的改变也会及时更新缓存的,所以显示也没有问题。现在保存的是css和静态文件图片,部分js。下次进入页面的时候不会再请请这些保存的东西,会直接从本地取,但是还有一点是,如果60秒时间到,他也不会去重新网络请求这些东西,60后秒的英文只不过请求服务器再次,(这可能返回304了,又在本地取了)。这一定要清楚.PS(只有删除缓存才会重新去服*务器获取CSS文件;静态文件)所以现在也能解决我们高并发那个问题。

直接配置缓存

private void initWebView() {    

        mWebView.getSettings().setJavaScriptEnabled(true);    
        mWebView.getSettings()setRenderPriority(RenderPriority.HIGH)。    
        。mWebView.getSettings()setCacheMode(WebSettings.LOAD_DEFAULT); //设置缓存模式     
        //开启DOM存储API功能     
        mWebView.getSettings()。setDomStorageEnabled(true);    
        //开启数据库存储API功能     
        mWebView.getSettings()。setDatabaseEnabled(true);     
        String cacheDirPath = getFilesDir()。getAbsolutePath()+ APP_CACAHE_DIRNAME;    
// String cacheDirPath = getCacheDir()。getAbsolutePath()+ Constant.APP_DB_DIRNAME;    
        Log.i(TAG,“cacheDirPath =+ cacheDirPath);    
        //设置数据库缓存路径     
        .mWebView.getSettings()setDatabasePath(cacheDirPath);    
        //设置应用程序缓存缓存目录     
        mWebView.getSettings()。setAppCachePath(cacheDirPath);    
        //开启Application缓存功能     
        mWebView.getSettings()。setAppCacheEnabled(true);    
    }   ```

清空缓存方法

package com.ihaveu.iuzuan.cardgame.util;

import android.content.Context;

import com.ihaveu.iuzuan.cardgame.base.BaseApplication;

import java.io.File;

/**
 * <p>Title: CacheWebViewManager</p >
 * <p>Description: TODO</p >
 * <p>Company: ihaveu</p >
 *
 * @author MaWei
 * @date 2018/3/22
 */
public class CacheWebViewManager {

    /** 缓存路径*/
    public static final String APP_CACAHE_DIRNAME = "/webcache";

    public static void clearWebViewCache(){
        //清理Webview缓存数据库
        try {
            BaseApplication.getContext().deleteDatabase("webview.db");
            BaseApplication.getContext().deleteDatabase("webviewCache.db");
        } catch (Exception e) {
            e.printStackTrace();
        }

        //WebView 缓存文件
        File oldCache = BaseApplication.getContext().getCacheDir();
        // 获取app_webview文件夹
        File oldWebview = BaseApplication.getContext().getDir("app_webview", Context.MODE_PRIVATE);


        //删除webview 缓存目录
        if(oldCache.exists()){
            deleteFile(oldCache);
        }
        //删除webview 缓存 缓存目录
        if(oldWebview.exists()){
            deleteFile(oldWebview);
        }
    }

    /**
     * 递归删除 文件/文件夹
     *
     * @param file
     */
    public static void deleteFile(File file) {

        LogUtil.d("delete file path=" + file.getAbsolutePath());

        if (file.exists()) {
            if (file.isFile()) {
                file.delete();
            } else if (file.isDirectory()) {
                File files[] = file.listFiles();
                for (int i = 0; i < files.length; i++) {
                    deleteFile(files[i]);
                }
            }
            file.delete();
        } else {
            LogUtil.d("delete file no exists " + file.getAbsolutePath());
        }
    }
}

完事了,这个只不过实在H5更新的时候清空一下缓存,整体方案就可以了。

现在介绍一下第一种,这种方案不但可以解决高并发,还可以解决加载Webview速度的问题,基本上就是顺开。
之前我也用的缓存技术,从缓存中去读数据打开webview但是速度还是不是特别的快不是很理想,知道我把东西都下到本地以后,才发现是真快!只不过这样是有缺点的。每次更新都要下载十几兆,所以也是有弊端的。但是速度真是上去了。

从网上查相关资料都是,用缓存技术,要不就是用腾讯的浏览器内核等,基本上效果不大。

下面我分享一下我的总结吧,为什么用缓存技术跟下载到本地取打开速度差别这么大呢,
先解释一下使用缓存技术,他是在第一次加载H5的时候,把静态文件缓存到磁盘或者内存中,第二次从缓存中读取,但是效果不理想,因为他要把所有的JS文件加载完毕以后,然后在加载数据的时候,进行对加载数据拦截,每一个都要拦截判断这个有没有缓存过,有缓存就去磁盘中读取,这个其实比较耗时的。当然比什么都不错要快点。第二个换个浏览器内核不快。网上说提速30%。其实没有。这两个加一起也不快。所以下载到本地,就是省略了webview访问服务器网络这一步,这是其一,还省略了一步,它拦截每个url后*在去判断去磁盘缓存还是网络取,省去了 读取的操作,所以效果是大大的提升了。

package com.ihaveu.iuzuan.cardgame.util;

import android.content.Context;
import android.os.Environment;
import android.util.Log;

import com.ihaveu.iuzuan.cardgame.base.BaseApplication;
import com.lzy.okgo.OkGo;
import com.lzy.okgo.callback.FileCallback;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import okhttp3.Call;
import okhttp3.Response;

/**
 * <p>Title: WebViewFileDown</p >
 * <p>Description: TODO</p >
 * <p>Company: ihaveu</p >
 *
 * @author MaWei
 * @date 2018/4/8
 */
public class WebViewFileDown {
    private static WebViewFileDown mWebViewFileDown;
    /** 保存文件名*/
    public static final String DOWN_LOAD_NAME = "allwebcache.zip";
    /** 下载路径*/
    public static final String DOWN_LOAD = "/allweb";
    /** 解压目录*/
    public static final String DECOM_LOAD = "/decom_load";

    public static WebViewFileDown getInstance(){
        if(mWebViewFileDown == null) {
            mWebViewFileDown = new WebViewFileDown();
        }
        return mWebViewFileDown;
    }

    /**
     * 下载地址
     * @param url
     */
    public void downLoadFile(String url){

        // TODO: 测试直接压缩用的
//        //获取压缩文件zip
//        File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + DOWN_LOAD + "/box.zip");
//        // 解压文件
//        unZipFolder(file,
//                Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + DECOM_LOAD);

        // TODO: 手机存储/Download/allweb文件夹下  
        //Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS); 
        OkGo.get(url).tag(this).execute(new FileCallback(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + DOWN_LOAD, DOWN_LOAD_NAME) {
            @Override
            public void onSuccess(File file, Call call, Response response) {
                Log.e("asdasd12312" , "下载成功");
                // 解压文件
                unZipFolder(file,
                        Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + DECOM_LOAD);
            }

            @Override
            public void downloadProgress(long currentSize, long totalSize, float progress, long networkSpeed) {
                super.downloadProgress(currentSize, totalSize, progress, networkSpeed);
                Log.e("asdasd12312" , "下载..........." +progress);

            }

            @Override
            public void onError(Call call, Response response, Exception e) {
                Log.e("asdasd12312" , "失败");

            }
        });
    }

    /**
     * 解压下载的zip包
     * 解压文件
     * 压缩路径
     */
    public void unZipFolder(File archive, String decompressDir) {
        try {
            BufferedInputStream bi;
            ZipFile zf = new ZipFile(archive);
            Enumeration e = zf.entries();
            while (e.hasMoreElements()) {
                ZipEntry ze2 = (ZipEntry) e.nextElement();
                String entryName = ze2.getName();
                String path = decompressDir + "/" + entryName;
                if (ze2.isDirectory()) {
                    Log.e("asdasd12312" , "正在创建解压目录 - " + entryName);
                    File decompressDirFile = new File(path);
                    if (!decompressDirFile.exists()) {
                        decompressDirFile.mkdirs();
                    }
                } else {
                    Log.e("asdasd12312" , "正在创建解压文件 - " + entryName);
                    String fileDir = path.substring(0, path.lastIndexOf("/"));
                    File fileDirFile = new File(fileDir);
                    if (!fileDirFile.exists()) {
                        fileDirFile.mkdirs();
                    }
                    BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(decompressDir + "/" + entryName));
                    bi = new BufferedInputStream(zf.getInputStream(ze2));
                    byte[] readContent = new byte[1024];
                    int readCount = bi.read(readContent);
                    while (readCount != -1) {
                        bos.write(readContent, 0, readCount);
                        readCount = bi.read(readContent);
                    }
                    bos.close();
                }
            }
            zf.close();
        } catch (IOException e) {
            Log.e("asdasd12312" ,"faile to unzip file");
        }
    }


    /**
     * 在更新的时候删除文件
     * 递归删除 文件/文件夹
     * @param file
     */
    public void deleteFile(File file) {
//          获取压缩路径文件夹
//        File mfile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
//                + "/decom_load");
//        获取文件zip
//        File mfile2 = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
//                + "/allweb");
//        WebViewFileDown.getInstance().deleteFile(mfile);
//        WebViewFileDown.getInstance().deleteFile(mfile2);

        LogUtil.d("delete file path=" + file.getAbsolutePath());

        if (file.exists()) {
            if (file.isFile()) {
                file.delete();
            } else if (file.isDirectory()) {
                File[] files = file.listFiles();
                for (int i = 0; i < files.length; i++) {
                    deleteFile(files[i]);
                }
            }
            file.delete();
        } else {
            LogUtil.d("delete file no exists " + file.getAbsolutePath());
        }
    }
}

**webview调用本地HTML:
备注一下,我们正常的项目,要把下载的h5zip文件放在Applaction.getCacheDir()或者getcacheFiles文件下,
这相对于比较安全的,因为这目录是放在android/data/data/包名/cahce 或者/file文件下
android/data/data/包名这个目录不root是看不到的 所以相对于比较安全的。**

webView.getSettings().setAllowFileAccess(true);// 设置允许访问文件数据
mWebView.loadUrl(“file:///mnt/sdcard/Download/decom_load/box/index.html#/openBox”);

发布了51 篇原创文章 · 获赞 78 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/weixin_39079048/article/details/79882431