首先先聲明一下工作中遇到的問題,我們項目在晚上九點的時候,是一個高峯期。由於服務器高併發沒有優化好,在短時間內要做一下解決方案。
還有一個是優化加載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”);