美文网首页
Android WebView加载优化

Android WebView加载优化

作者: Dotry | 来源:发表于2020-08-15 20:57 被阅读0次

    1.前言

    最近几年关于原生WebView与H5混合开发的项目越来越多,这种开发带来了很多便利,但也会有一些缺点,比如说通过WebView加载H5会有一定的卡顿现象,会影响用户体验。下面本文就此问题一一展开讨论。

    2. 场景

    根据日常需求一般是通过webView.loadUrl()方法加载指定的网页,其大概流程如下:

    image.png
    • 创建WebView:通过Java代码创建WebView,设置相关的参数和属性
    • 发起请求:通过底层内核会先检查缓存,然后决定是否创建对应的请求,建立对应的http请求。
    • 页面解析:下载对应的DOM树和相关资源。
    • 内容处理:根据上面的资源和树形结构,进行渲染展示。
      以上是整个网页请求的大概过程,优化也是在这几个过程中分别作出细致对应的方案。

    2.1 创建WebView

    WebView创建的时机会对整个流程是有影响的,Webview的启动相对来说比较耗时,因此这个时候我们可以采用提前启动Webview,这样就可以在加载网页的时候已经准备好。比如我在做Browser的时候发现有Native页面点击网页icon,启动相对来说比较慢,那么采用的策略就是进入Native页面后,一段时间后提前创建Webview。一开始本以为这样就可以,但是浏览器在tab页面可以新建多个网页加载器,也就是会有多个webview。显然光是提前创建也不太好,后面采用创建一个WebView的池子来管理这些Webview

    /**
     * webview 复用池
     */
    public class WebViewPool {
        private static final String DEMO_URL = "https://www.baidu.com";
        private static final String APP_CACAHE_DIRNAME = "webCache";
        private static List<WebView> available = new ArrayList<>();
        private static List<WebView> inUse = new ArrayList<>();
        private static final byte[] lock = new byte[]{};
        private static int maxSize = 2;
        private int currentSize = 0;
        private static long startTimes = 0;
        private static volatile WebViewPool instance = null;
     
        public static WebViewPool getInstance() {
            if (instance == null) {
                synchronized (WebViewPool.class) {
                    if (instance == null) {
                        instance = new WebViewPool();
                    }
                }
            }
            return instance;
        }
     
        /**
         * Webview 初始化
         * 最好放在application oncreate里
         */
        public static void init(Context context) {
            for (int i = 0; i < maxSize; i++) {
                WebView webView = new WebView(context);
                initWebSeting(context, webView);
                //webView.loadUrl(DEMO_URL);
                available.add(webView);
            }
        }
     
        /**
         * 获取webview
         */
        public WebView getWebView(Context context) {
            synchronized (lock) {
                WebView webView;
                if (available.size() > 0) {
                    webView = available.get(0);
                    available.remove(0);
                    currentSize++;
                    inUse.add(webView);
                } else {
                    webView = new WebView(context);
                    initWebSeting(context, webView);
                    inUse.add(webView);
                    currentSize++;
                }
                return webView;
            }
        }
     
        /**
         * 回收webview ,不解绑
         *
         * @param webView 需要被回收的webview
         */
        public void removeWebView(WebView webView) {
            webView.loadUrl("");
            webView.stopLoading();
            webView.setWebChromeClient(null);
            webView.setWebViewClient(null);
            webView.clearCache(true);
            webView.clearHistory();
            synchronized (lock) {
                inUse.remove(webView);
                if (available.size() < maxSize) {
                    available.add(webView);
                } else {
                    webView = null;
                }
                currentSize--;
            }
        }
     
        /**
         * 回收webview ,解绑
         *
         * @param webView 需要被回收的webview
         */
        public void removeWebView(ViewGroup viewGroup, WebView webView) {
            viewGroup.removeView(webView);
            webView.loadUrl("");
            webView.stopLoading();
            webView.setWebChromeClient(null);
            webView.setWebViewClient(null);
            webView.clearCache(true);
            webView.clearHistory();
            synchronized (lock) {
                inUse.remove(webView);
                if (available.size() < maxSize) {
                    available.add(webView);
                } else {
                    webView = null;
                }
                currentSize--;
            }
        }
     
        /**
         * 设置webview池个数
         *
         * @param size webview池个数
         */
        public void setMaxPoolSize(int size) {
            synchronized (lock) {
                maxSize = size;
            }
        }
     
        private static void initWebSeting(Context context, WebView webView) {
            ViewGroup.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.MATCH_PARENT);
            webView.setLayoutParams(params);
            WebSettings webSettings = webView.getSettings();
            //如果访问的页面中要与Javascript交互,则webview必须设置支持Javascript
            webSettings.setJavaScriptEnabled(true);
            // 若加载的 html 里有JS 在执行动画等操作,会造成资源浪费(CPU、电量)
            // 在 onStop 和 onResume 里分别把 setJavaScriptEnabled() 给设置成 false 和 true 即可
     
            //支持插件
            //        webSettings.setPluginsEnabled(true);
     
            //设置自适应屏幕,两者合用
            webSettings.setUseWideViewPort(true); //将图片调整到适合webview的大小
            webSettings.setLoadWithOverviewMode(true); // 缩放至屏幕的大小
     
            //缩放操作
            webSettings.setSupportZoom(true); //支持缩放,默认为true。是下面那个的前提。
            webSettings.setBuiltInZoomControls(true); //设置内置的缩放控件。若为false,则该WebView不可缩放
            webSettings.setDisplayZoomControls(false); //隐藏原生的缩放控件
     
            //其他细节操作
            webSettings.setCacheMode(WebSettings.LOAD_DEFAULT); //关闭webview中缓存
            webSettings.setAllowFileAccess(true); //设置可以访问文件
            webSettings.setJavaScriptCanOpenWindowsAutomatically(true); //支持通过JS打开新窗口
            webSettings.setLoadsImagesAutomatically(true); //支持自动加载图片
            webSettings.setDefaultTextEncodingName("utf-8");//设置编码格式
     
            webSettings.setDomStorageEnabled(true); // 开启 DOM storage API 功能
            webSettings.setDatabaseEnabled(true);   //开启 database storage API 功能
            webSettings.setAppCacheEnabled(true);//开启 Application Caches 功能
     
            String cacheDirPath = context.getFilesDir().getAbsolutePath() + APP_CACAHE_DIRNAME;
            webSettings.setAppCachePath(cacheDirPath); //设置  Application Caches 缓存目录
     
            //页面白屏问题
            webView.setBackgroundColor(ContextCompat.getColor(context, android.R.color.transparent));
            webView.setBackgroundResource(R.color.white);
        }
    }
    

    需要Webview的时候直接在此pool中取即可。

    2.2 发起请求

    在Webview真正发起请求的之前,Webview会进行缓存检查,而对于Webview来说可以可以设置缓存webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);
    在DOM树和相关图片资源下载的时候,这个其实是相对来说比较耗时的,那么一般有以下几个优化的思路

    • DOM树形成固定的模板,通过http请求出具体数据,填充到模板
    • 资源拦截:通过shouldInterceptRequest拦截一些图片和css资源,通过本地返回。
    • 图片加载: 在Webview进行初始化的时候设置默认不可以加载图片,在pagefinished方法中设置图片自动加载。

    2.3 内容处理

    关于内容处理这个其实需要服务器配合,简单来说当用户通过WebView来加载某个网页A,此时网页会出现一些内容,当用户点击此网页中内容的时候会进入下一页B。那么在用户刚进入A的时候,此时服务器是可以预知到下一个页面B的内容,因为B的链接是知道的,那么此时服务器可以通过预加载的方式请求到相关内容,内容回来之后无论是置于CDN还是推到客户端都可以随业务来定,这样相对来说是会提高加载速度的。

    总结

    整体来说WebView的加载优化大概以上几个方向:

    • 活用缓存
    • 提前加载
    • 尝试拦截
      这是Webview这块可以处理的,当然如果可以自己处理内核,那么可以优化的空间会更多,在此就不做展开。

    相关文章

      网友评论

          本文标题:Android WebView加载优化

          本文链接:https://www.haomeiwen.com/subject/afjerktx.html