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;

/**
 * 

Title: CacheWebViewManager

*

Description: TODO

*

Company: ihaveu

* * @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;

/**
 * 

Title: WebViewFileDown

*

Description: TODO

*

Company: ihaveu

* * @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”);

你可能感兴趣的:(技术分析)