Android WebView加载优化

待兔 等级 1214 0 0
标签: webview移动端

1.前言

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

2. 场景

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

Android WebView加载优化

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加载优化
1.前言 最近几年关于原生WebView与H5混合开发的项目越来越多,这种开发带来了很多便利,但也会有一些缺点,比如说通过WebView加载H5会有一定的卡顿现象,会影响用户体验。下面本文就此问题一一展开讨论。 2. 场景 根据日常需求一般是通过webView.loadUrl()方法加载指定的网页,其大概流程如下: (https://i
Android深入理解WebView——上
摘要 -- 作为Android开发者,我们都知道在手机中内置了一款高性能 webkit 内核浏览器,在 SDK 中封装为一个叫做 WebView 组件。今天就为大家讲讲Android中WebView的详细使用方法         本文原创,转载请注明地址:[http://blog.kymjs.com/](https://www.oschina.net/
UIWebView背景透明的方法
1.首先UIWebView背景透明 // set background transparent, also can set it in nib filewebView\_.backgroundColor = \[UIColor clearColor\]; webView\_.opaque = NO; 2.隐藏拖拽webview时上下的两个有阴影效果的su
android WebView 使用实例
主布局文件: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
APPIUM 小程序webview问题
小程序许多界面都是hybrid,有些webview页面用uiautomator viewer查看不到元素,这里就要 获取webview  的pagesource了 1.环境:   需要确定appium\_chromedriver的版本和微信的webview版本对应: 获取微信的webview版本信息,打开x5调试debugx5.qq.com 在信息页面
Android WebView
[Skip to content](https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fdeveloper.android.com%2Freference%2Fandroid%2Fwebkit%2FWebView.html%23top) [![Android](https://develop
Android WebView实现js与java交互
刚学Android用WebView来做应用 Android Studio下载: [http://www.android-studio.org/](https://www.oschina.net/action/GoToLink?url=http%3A%2F%2Fwww.android-studio.org%2F) 写此文时最新稳定版是2.3.3,预览版3.
Android webview使用详解
1\. 打开网页时不调用系统浏览器, 而是在本WebView中显示: ![复制代码](http://static.oschina.net/uploads/img/201603/18112202_IqHR.gif) mWebView.setWebViewClient(new WebViewClient(){       @Override 
Android与H5交互 原理与对比
[原文:  https://www.jianshu.com/p/345f4d8a5cfa](https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fwww.jianshu.com%2Fp%2F345f4d8a5cfa) 1.Android调用JS的方法有2种:  -----------------
Android使用WebView加载H5页面播放视频音频,退出后还在播放问题解决
Android中经常会使用到WebView来加载H5的页面,如果H5页面中有音频或者视频的播放时,还没播放完就退出界面,这个时候会发现音频或者视频还在后台播放,这就有点一脸懵逼了,下面是解决方案: 方案一: 在webview所在的activity中的onPause()和onResume()方法中写上两句话。 public void on
Electron webview完全指南
感谢支持ayqy个人订阅号,每周义务推送1篇(only _unique_ one)原创精品博文,话题包括但不限于前端、Node、Android、数学(WebGL)、语文(课外书读后感)、英语(文档翻译)                如果觉得弱水三千,一瓢太少,可以去 http://blog.ayqy.net 看个痛快     一.webview标签
Flutter
在Flutter 加载网页?也是有WebView的哦,和Android一样 1.添加依赖 dependencies: flutter\_webview\_plugin: ^0.2.1+2 2.导入库 import 'import 'package:flutter\_webview\_plugin/flutter\_webview\_plug
Flutter 实现原理及在马蜂窝的跨平台开发实践
一直以来,跨平台开发都是困扰移动客户端开发的难题。 在马蜂窝旅游 App 很多业务场景里,我们尝试过一些主流的跨平台开发解决方案, 比如 WebView 和 React Native,来提升开发效率和用户体验。但这两种方式也带来了新的问题。 比如使用 WebView 跨平台方式,优点确实非常明显。基于 WebView 的框架集成了当下 Web 开发的诸多
JS与iOS之间的通信
JS与iOS之间的通信,主要运用两个方法:(PhoneGap框架也是基于此原理) 1、UIWebView的 stringByEvaluatingJavaScriptFromString方法 2、UIWebViewDelegate的 \- (BOOL)webView:(UIWebView \*)webView shouldStartLoadWithReq
PHP开发Linux桌面应用和Android应用思路
PHP7中用opcache.file\_cache导出脚本opcode实现源代码保护 [http://my.oschina.net/eechen/blog/539995](http://my.oschina.net/eechen/blog/539995) 下载PHPDroid: 基于WebView和PHP内置HTTP服务器开发Android应用