AndroidStudio-3.2.1(二十五) WebView—性能优化

WebView在混合开发中扮演着重要角色,使用的业务场景也十分复杂,因此出现的各种问题及效率优化需求也很多,当然google本身也在不断完善这个控件。本文总结几个常见问题:

页面加载速度优化

1 webview初始化问题

在webveiw里加载网页,感觉比PC端慢好多。这是因为webview第一次使用时,需要先初始化浏览器内核,这一步大概耗时几百ms,然后才能继续后面的请求网址、建立连接等一系列操作。也就是说:在初始化webview时,后面的进程完全是阻塞的。那么如何优化呢?美团技术组给出了3种业界常用的方法:

1、全局WebView:在app刚启动时,就初始化一个全局的WebView待用,并隐藏;
当用到WebView时,直接使用这个WebView加载对应网页,并展示。
2、全局WebView+预加载资源:在Android 的BaseApplication里初始化一个WebView对象(用于加载常用的H5页面资源);当需使用这些页面时再从BaseApplication里取过来直接使用
全局WebView对应问题:
1)额外的内存消耗,一个webview大概占几十M的内存吧。
2)页面间跳转需要清空上一个页面的痕迹,更容易内存泄露。

3、客户端代理数据请求:在初始化webview的同时,使用native请求网页数据,然后将返回数据使用webview渲染出来。

2 改善用户体验

从用户体验上下点功夫:

1、继承webview控件,加一个进度条,结合WebChromeClient提供的方法onProgressChanged,提示页面加载进度。

2、对于图片加载,可以设置 webView.getSettings().setLoadsImagesAutomatically(false);然后在onPageFinished方法中,再设置为ture:webView.getSettings().setLoadsImagesAutomatically(true);

3 利用缓存机制

WebView加载过一次页面后,就会在本地留下缓存,利用缓存文件,可以提高以后的页面加载速度。
WebView自带的缓存机制有4种:

  • 1、浏览器缓存机制: 这是浏览器内核的机制,一般都是标准的实现,使用Cache-ControlLast-Modified来实现控制缓存有效时间,以及在缓存失效后向服务查询是否有更新。
  • 2、Application Cache 缓存机制:以文件为单位进行缓存,专门为 Web App离线使用而开发的缓存机制。但AppCache 是对浏览器缓存机制 的补充,不是替代。
 		WebSettings settings = webView.getSettings();
        String cacheDirPath = context.getFilesDir().getAbsolutePath()+"cache/";
        settings.setAppCachePath(cacheDirPath); // 1. 设置缓存路径
        settings.setAppCacheMaxSize(20*1024*1024); // 2. 设置缓存大小
        settings.setAppCacheEnabled(true); // 3. 开启Application Cache存储机制
  • 3、Dom Storage 缓存机制:手动通过存储字符串的 Key - Value 对来提供,似于 Android 的 SharedPreference机制。
        WebSettings settings = webView.getSettings();
        settings.setDomStorageEnabled(true);
  • 4、Indexed Database 缓存机制:属于 NoSQL 数据库,通过存储字符串的 Key - Value 对来提供。Android 4.4之后启用。
	    WebSettings settings = webView.getSettings();
        settings.setJavaScriptEnabled(true);// 只需设置支持JS就自动打开IndexedDB存储机制

WebView使用缓存的模式:

  	   // LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据
      // LOAD_NO_CACHE: 不使用缓存,只从网络获取数据.
      // LOAD_DEFAULT: (默认)根据cache-control决定是否从网络上取数据。
      // LOAD_CACHE_ELSE_NETWORK,只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据。
      WebView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);

根据业务场景使用如下:

  • 存储静态资源文件(js、css):使用浏览器缓存及Application Cache
  • 存储临时、简单数据:使用Dom Storage 缓存
  • 存储复杂、数据量大的结构化数据:使用Indexed Database 缓存

自定义缓存使用:
1、事先将更新频率较低、常用 & 固定的H5静态资源 文件(如JS、CSS文件、图片等) 放到本地;
2、使用shouldInterceptRequest拦截H5页面的资源网络请求 并进行检测;
3、如果检测到本地具有相同的静态资源 ,就直接从本地读取进行替换,而不发送该资源的网络请求到服务器。

 mWebview.setWebViewClient(new WebViewClient() {
             // API 21 以下用shouldInterceptRequest(WebView view, String url)
            @Override
            public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
                // 假设要拦截对logo.gif的网络请求
                if (url.contains("logo.gif")) {
                    InputStream is = null;
                    try {
                        is =getApplicationContext().getAssets().open("images/abc.png");
                    } catch (IOException e) {
                        e.printStackTrace();
                    }

                    WebResourceResponse response = new WebResourceResponse("image/png",
                            "utf-8", is);
                    return response;
                }
                return super.shouldInterceptRequest(view, url);
            }
            
           // API 21 以上用shouldInterceptRequest(WebView view, WebResourceRequest request)
            @TargetApi(Build.VERSION_CODES.LOLLIPOP)
            @Override
            public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {

                if (request.getUrl().toString().contains("logo.gif")) {
                    InputStream is = null;
                    try {
                        is = getApplicationContext().getAssets().open("images/abc.png");
                    } catch (IOException e) {
                        e.printStackTrace();
                    }

                    WebResourceResponse response = new WebResourceResponse("image/png",
                            "utf-8", is);
                    return response;
                }
                return super.shouldInterceptRequest(view, request);
            }
};

上述放到本地的静态资源的更新姿势:

  • 发布新版本安装更新
  • 增量更新:在用户处于WIFI环境时让服务器推送到本地
4 优化H5页面

对于HTML页面的加载与解析以及如何优化也需要仔细分析,这里不做详细探讨。

硬件加速及相关问题

开启硬件加速会提升app的响应性能,尤其对WebView、ListView等各类集合View有明显效果。但是开启了硬件加速也会带来一些问题,一般表现为WebView的显示异常,比如:白屏、花屏、字符错乱等。因此当出现这些问题的时候,不妨从硬件加速角度排查一下。

硬件加速开启方式
  • Application级别

     
    
  • Activity级别

     
  • window级别(目前为止,Android还不支持在Window级别关闭硬件加速。)

     getWindow().setFlags(
           WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
           WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
    
  • View级别

     view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
    
硬件加速与layertype的关系

WebView继承View,View中有三种layer type:

  • LAYER_TYPE_NONE:无渲染
  • LAYER_TYPE_SOFTWARE:无论硬件加速开关:都把WebView渲染到一个Bitmap上,
  • LAYER_TYPE_HARDWARE:硬件加速开:把WebView渲染到一个FBO(Framebuffer Object)上;

因此:
硬件加速开 + LAYER_TYPE_HARDWARE
硬件加速关 + LAYER_TYPE_SOFTWARE

硬件加速临时关闭

在执行某些操作时,会导致webview闪烁,比如侧滑、上滑菜单出现/消失时,这时需要在滑动开始前后,开启/关闭硬件加速。

  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
        webview.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
    }

ViewPager里非首屏WebView点击问题

如果ViewPager里放了多个WebView,则除了首屏的Webview外,其余的webview点击网页上的按钮时,界面上没有交互效果,但是事件已执行。我使用onJsAlert方法测试的:

 webView.setWebChromeClient(new WebChromeClient(){
            @Override
            public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
                Toast.makeText(getActivity(),message,Toast.LENGTH_SHORT).show();
                result.cancel();
                return true;
            }
        });

解决办法:继承Webview,重写onTouchEvent方法。如下:

    //必须重载此构造函数,否则无法正确加载
    public MyWebview(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            onScrollChanged(getScrollX(), getScrollY(), getScrollX(), getScrollY());
        }
        return super.onTouchEvent(event);
    }

安全性设置

解决WebView的内存泄漏

完全防止内存泄露,需要在Activity中初始化WebView.

    mWebView = new WebView(getApplicationContext());
    mWebView.setLayoutParams(params);
    mLayout.addView(mWebView);

在 Activity 销毁 WebView 的时候,先让 WebView 加载null内容,然后移除 WebView,再销毁 WebView,最后置空。

@Override
protected void onDestroy() {
    if (mWebView != null) {
        mWebView.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);
        mWebView.clearHistory();
        ((ViewGroup) mWebView.getParent()).removeView(mWebView);
        mWebView.destroy();
        mWebView = null;
    }
    super.onDestroy();
}
本地文件访问权限控制

1、关闭本地密码存储功能。如果开启,密码会被明文保到 /data/data/com.package.name/databases/webview.db 中,这样就有被盗取密码的危险

mWebView.setSavePassword(false)

2、setAllowFileAccess 是否允许本地文件(file协议)的访问。

	//对于不需要使用 file 协议的应用,禁用file 协议;
  setAllowFileAccess(false); 
  
	//对于需要使用 file 协议的应用,禁止加载 JavaScript。
  setAllowFileAccess(true); 
  if (url.startsWith("file://") {
      setJavaScriptEnabled(false);
  } else {
      setJavaScriptEnabled(true);
  }

3、setAllowFileAccessFromFileURLs 是否允许通过 file url 加载的js代码读取其他的本地文件 ,Android 4.1后默认禁止。
4、setAllowUniversalAccessFromFileURLs 是否允许通过 file url 加载的 js代码访问其他的源(包括http、https等源),Android 4.1后默认禁止.

你可能感兴趣的:(Android入门)