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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章