首先先声明一下工作中遇到的问题,我们项目在晚上九点的时候,是一个高峰期。由于服务器高并发没有优化好,在短时间内要做一下解决方案。
还有一个是优化加载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”);