WebViewPool.java 12.3 KB
package com.people.webview.ui;

import android.content.Context;
import android.content.MutableContextWrapper;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Looper;
import android.os.MessageQueue;
import android.text.TextUtils;
import android.view.View;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebResourceError;
import android.webkit.WebResourceRequest;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import androidx.annotation.Nullable;

import com.google.gson.Gson;
import com.people.webview.javabridge.BridgeJavascriptInterface;
import com.wd.base.log.Logger;
import com.wd.foundation.wdkit.base.BaseApplication;
import com.wd.foundation.wdkit.system.DeviceUtil;
import com.wd.foundation.wdkit.utils.SpUtils;
import com.wd.foundation.wdkitcore.tools.AppContext;
import com.wd.foundation.wdkitcore.tools.ArrayUtils;

import java.io.File;
import java.util.Stack;

/**
 * @author qtiansheng
 * @date 2023/01/06
 * @Description webview缓存池
 * @doc
 */
public class WebViewPool {
    private WebViewPool(){}

    private String TAG= "WebViewPool";

    /**
     * 图文webview类型
     */
    public static final String TEMPLATE_ARTICLE = "article";
    /**
     * 专题webview类型
     */
    public static final String TEMPLATE_SUBJECT = "subject";

    /**
     * 图文详情页模板地址
     */
    public static final String WEB_VIEW_LOCAL_PATH = "/android_asset/apph5/index.html";


    private static final int CACHED_WEBVIEW_MAX_NUM = 2;
    private static final Stack<NativeWebView> mCachedWebViewStackArticle = new Stack<>();

    private static final Stack<NativeWebView> mCachedWebViewStackSubject = new Stack<>();
    private static WebViewPool mInstance = null;

    public static WebViewPool getInstance(){
        if (null == mInstance){
            synchronized (WebViewPool.class) {
                if (null == mInstance) {
                    mInstance = new WebViewPool();
                }
            }
        }
        return mInstance;
    }

    /**
     * 创建WebView实例
     * 用了applicationContext
     */
    public void preload(String template) {
        Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
            @Override
            public boolean queueIdle() {
                if(TEMPLATE_ARTICLE.equals(template) && mCachedWebViewStackArticle.size() < CACHED_WEBVIEW_MAX_NUM){
                    mCachedWebViewStackArticle.push(createWebView(TEMPLATE_ARTICLE));
                }else if(TEMPLATE_SUBJECT.equals(template) && mCachedWebViewStackSubject.size() < CACHED_WEBVIEW_MAX_NUM){
                    mCachedWebViewStackSubject.push(createWebView(TEMPLATE_SUBJECT));
                }else if( mCachedWebViewStackSubject.size() < CACHED_WEBVIEW_MAX_NUM){
                    mCachedWebViewStackArticle.push(createWebView(TEMPLATE_ARTICLE));
                }

                return false;
            }
        });
    }

    /**
     * 创建WebView实例
     * 用了applicationContext
     */
    public void pushWebview(String template,NativeWebView webView) {
        if(TEMPLATE_ARTICLE.equals(template) && mCachedWebViewStackArticle.size() < CACHED_WEBVIEW_MAX_NUM){
            mCachedWebViewStackArticle.push(webView);
        }
    }


    private NativeWebView createWebView(String type) {
        NativeWebView mWebView = new NativeWebView(new MutableContextWrapper(BaseApplication.getInstance()));
        WebSettings mWebSettings = mWebView.getSettings();
        /**
         * 增加useragent尾部标志;tag=anhuiAPP,以供h5区分渠道
         */
        mWebSettings.setUserAgentString(getUA(mWebView));

        mWebSettings.setSupportZoom(false);
        mWebSettings.setDefaultTextEncodingName("utf-8");
        mWebSettings.setUseWideViewPort(true);
        mWebSettings.setLoadsImagesAutomatically(true);
        mWebSettings.setJavaScriptEnabled(true);
        mWebSettings.setAllowFileAccess(true);
        mWebSettings.setBlockNetworkLoads(false);
        mWebSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
        mWebSettings.setMediaPlaybackRequiresUserGesture(false);
        mWebSettings.setMixedContentMode(WebSettings.LOAD_DEFAULT);
        //关闭硬件加速,防止超过缓存卡死问题
        mWebView.setLayerType(View.LAYER_TYPE_NONE, null);

        mWebSettings.setDatabaseEnabled(true);
        mWebSettings.setAllowFileAccess(true);
        mWebSettings.setDomStorageEnabled(true);
        // 1. 设置缓存路径
        String cacheDirPath = AppContext.getContext().getFilesDir().getAbsolutePath()+"/cache/web/";
        mWebSettings.setAppCachePath(cacheDirPath);
        mWebView.getSettings().setDatabasePath(cacheDirPath);
        // 2. 设置缓存大小为200M
        mWebSettings.setAppCacheMaxSize(200*1024*1024);
        // 3. 开启Application Cache存储机制
        mWebSettings.setAppCacheEnabled(true);
        /**
         * LOAD_DEFAULT:使用默认的缓存模式,即按照网站的缓存策略来加载网页。
         * LOAD_NO_CACHE:不使用缓存,每次都重新从网络上获取数据。
         * LOAD_CACHE_ONLY:只使用缓存,不从网络上获取数据。
         * LOAD_CACHE_ELSE_NETWORK:会先使用缓存数据,如果缓存数据过期了再从网络上获取数据。
         */
        mWebSettings.setCacheMode(WebSettings.LOAD_DEFAULT);
        mWebSettings.setSupportMultipleWindows(false);
        mWebSettings.setJavaScriptCanOpenWindowsAutomatically(true);

        mWebView.setVerticalScrollBarEnabled(false);
        mWebView.setHorizontalScrollBarEnabled(false);
        //隐藏webview 右侧滑动条
        mWebView.setScrollbarFadingEnabled(false);


        if(TEMPLATE_SUBJECT.equals(type)) {
            return mWebView;
        }

        mWebView.setGson(new Gson());
        mWebView.setWebChromeClient(new WebChromeClient() {

            @SuppressWarnings("unused")
            public void openFileChooser(ValueCallback<Uri> uploadMsg, String AcceptType, String capture) {
                this.openFileChooser(uploadMsg);
            }

            @SuppressWarnings("unused")
            public void openFileChooser(ValueCallback<Uri> uploadMsg, String AcceptType) {
                this.openFileChooser(uploadMsg);
            }

            public void openFileChooser(ValueCallback<Uri> uploadMsg) {
            }

            @Override
            public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {
                return true;
            }

            @Nullable
            @Override
            public Bitmap getDefaultVideoPoster() {
                return Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
            }

            @Override
            public void onProgressChanged(WebView view, int newProgress) {
//                NBSWebChromeClient.initJSMonitor(view, newProgress);
                super.onProgressChanged(view, newProgress);
            }
        });
        mWebView.setWebViewClient(new WebViewClient() {
            @Override
            public void onPageStarted(WebView view, String url, Bitmap favicon) {
                super.onPageStarted(view, url, favicon);
//                Log.d(TAG, "onPageStarted time=" + System.currentTimeMillis());
            }

            @Override
            public void onPageFinished(WebView view, String url) {
                super.onPageFinished(view, url);
                int height = (int) (view.getContentHeight() * view.getScale());
                Logger.t(TAG).d("webViewHeight=======>onPageFinished:"+height);
            }

            @Override
            public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
                return super.shouldOverrideUrlLoading(view, request);
            }

            @Override
            public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
                super.onReceivedError(view, request, error);
            }

            @Override
            public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
                super.onReceivedError(view, errorCode, description, failingUrl);
            }

        });
        //图文详情本地H5模版不需要白名单控制
        mWebView.addJavascriptInterface(new BridgeJavascriptInterface(mWebView.getCallbacks(),
                mWebView), "WebViewJavascriptBridge");

        String h5path = WEB_VIEW_LOCAL_PATH;
        String h5TempVersion = SpUtils.getH5Template();
        Logger.t(TAG).d("h5TempVersion="+ h5TempVersion);
        if(h5TempVersion.startsWith("h5web1")){
            h5path =  new File(AppContext.getContext().getCacheDir(), "h5web1").getAbsolutePath() + "/index.html";
        }else if(h5TempVersion.startsWith("h5web2")){
            h5path = new File(AppContext.getContext().getCacheDir(), "h5web2").getAbsolutePath()+ "/index.html";
        }else {
            h5path = WEB_VIEW_LOCAL_PATH;
        }
        Logger.t(TAG).d("h5path1="+ h5path);
        if(!TextUtils.isEmpty(h5path) && doesFileExist(h5path)){
            h5path = "file://" + h5path;
            Logger.t(TAG).d("h5path2 ="+ h5path);
        }else{
            h5path = "file://" + WEB_VIEW_LOCAL_PATH;
            Logger.t(TAG).d("h5path3="+ h5path);
        }

        mWebView.loadUrl(h5path);
        return mWebView;
    }

    public boolean doesFileExist(String filePath) {
        File file = new File(filePath);
        return file.exists();
    }


    /**
     * 获取UA信息
     * @return
     */
    public  String getUA(WebView mWebView){
        try {
            WebSettings mWebSettings = mWebView.getSettings();
            String userAgentString = mWebSettings.getUserAgentString();
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append(userAgentString);
            stringBuilder.append(";tag=dailyChinaNewspaper");
            stringBuilder.append("&version=" + DeviceUtil.getVersionName());
            stringBuilder.append("&platform=Android");
            if(SpUtils.isNightMode()){
                stringBuilder.append("&darkMode=dark");
            }
            return stringBuilder.toString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }

    /**
     * 从缓存池中获取合适的WebView
     *
     * @param context activity context
     * @param context template 模板 article是图文,subject是专题
     * @return WebView
     */
    public NativeWebView getWebView(Context context,String template) {
        if(TEMPLATE_SUBJECT.equals(template) ) {
            // 为空,直接返回新实例
            if (mCachedWebViewStackSubject == null || mCachedWebViewStackSubject.isEmpty()) {
                NativeWebView web = createWebView(TEMPLATE_SUBJECT);
                MutableContextWrapper contextWrapper = (MutableContextWrapper) web.getContext();
                contextWrapper.setBaseContext(context);
                return web;
            }
            NativeWebView webView = mCachedWebViewStackSubject.pop();
            // webView不为空,则开始使用预创建的WebView,并且替换Context
            MutableContextWrapper contextWrapper = (MutableContextWrapper) webView.getContext();
            contextWrapper.setBaseContext(context);
            return webView;
        }else{
            // 为空,直接返回新实例
            if (mCachedWebViewStackArticle == null || mCachedWebViewStackArticle.isEmpty()) {
                NativeWebView web = createWebView(TEMPLATE_ARTICLE);
                MutableContextWrapper contextWrapper = (MutableContextWrapper) web.getContext();
                contextWrapper.setBaseContext(context);
                return web;
            }
            NativeWebView webView = mCachedWebViewStackArticle.pop();
            // webView不为空,则开始使用预创建的WebView,并且替换Context
            MutableContextWrapper contextWrapper = (MutableContextWrapper) webView.getContext();
            contextWrapper.setBaseContext(context);
            return webView;
        }

    }

    /**
     * 清空图文模版缓存
     */
    public void clearCachedWebViewStackArticle(){
        if(ArrayUtils.isNotEmpty(mCachedWebViewStackArticle)){
            mCachedWebViewStackArticle.clear();
        }
    }






}