美文网首页
Android中WebView性能优化

Android中WebView性能优化

作者: 奕晴天 | 来源:发表于2017-09-01 21:37 被阅读0次

前言

  • 在Android在使用H5开发具有快速开发的特点,所以现在大多数APP都采用了原生Android+WebView的Hybrid开发模式。
  • 缺点:Android下的WebView有一些痛点,由于受到Android手机的碎片化原因,性能低下,加载速度慢和浪费流量等缺点。
  • 今天我们就针对WebView中的性能问题进行优化。

WebView首次启动优化-常用资源预加载

  • 提前加载JS、CSS、图片资源,等H5页面加载完成后进行替换。
  • 原理: WebView中引入了ShouldInterceptRequest方法可以实现,需要注意的是该方法API11-API21和API21之后不同,需要做兼容性处理。
例子
  • 事先将一些更新频率比较低的常用资源(JS、CSS、图片资源)放置在本地,然后通过拦截H5对其资源进行本地化加载。
  • 加载网址:http://ip.cn/
  • 替换H5页面中的资源图片:http://s.ip-cdn.com/img/logo.gif
  • 代码如下:
webview.loadUrl("http://ip.cn/");//加载http://ip.cn/
webview.setWebViewClient(new WebViewClient(){
  @Override
  public boolean shouldOverrideUrlLoading(WebView view, String url) {
       view.loadUrl(url);
       return true;
  }

  //api21以下
  @Override
  public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
       if(url.contains("logo.gif")){
          InputStream is = null;
          try {
                 is = getApplicationContext().getAssets().open("images/index.jpg");
          } catch (IOException e) {
                 e.printStackTrace();
          }
          WebResourceResponse response = new WebResourceResponse("image/jpg","utf-8",is);
                    return response;
     }
          return super.shouldInterceptRequest(view, url);
   }

  //api21以上
  @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/index.jpg");
          } catch (IOException e) {
                e.printStackTrace();
          }
          WebResourceResponse response = new WebResourceResponse("image/jpg","utf-8",is);
              return response;
       }
              return super.shouldInterceptRequest(view, request);
  }
});

  • 替换前的图片
图片.png
  • 替换后的图片
图片.png

常用JS本地化及延迟加载

  • WebView渲染H5页面性能差归结于两个原因:
    1.JS解析过程比较复杂,解析速度比较慢导致。
    2.Android设备碎片化导致设备性能参差不齐,导致Android手机硬件部分无法很好的支持WebView的渲染。
  • 解决方案:将一些常用的JS脚步本地化处理,将JS文件编写好后放置在asserts文件夹中,直接打包到APK中。在WebView调用OnPageFinished()方法后进行加载。

WebView二次启动优化

  • 我们可以为WebView加入缓存机制,在二次启动的时候,首先去缓存中进行命中,是否可以命中缓存,不可以的时候才进行网络情况,可以进一步的优化WebView的启动速度。
  • 这里有几种比较常用的方案:
WebView的缓存模式
  • LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据。
  • LOAD_DEFAULT: (默认)根据cache-control决定是否从网络上取数据。
  • LOAD_NO_CACHE: 不使用缓存,只从网络获取数据。
  • LOAD_CACHE_ELSE_NETWORK,只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据。
//优先使用缓存:
WebView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
// 不使用缓存: 
WebView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
  • 建议:
    判断是否有网络,有的话,使用LOAD_DEFAULT,
    无网络时,使用LOAD_CACHE_ELSE_NETWORK。
浏览器缓存机制-前端开发人员负责(静态文件缓存)
  • 在HTTP 协议头里有这样一个字段 Cache-Control是用来控制缓存的。
 //代表本地缓存,且从发起请求算起,接下来500s,如果有该资源请求都不会进行HTTP请求,而是读取本地的缓存
 Cache-Control:max-age=500
Dom Storage
  • 主要用于存储一些临时、简单的数据缓存和cookie。
  • DOM Storage 分为 SessionStorage 和 LocalStorage。
  • SessionStorage是临时性的,在WebView关闭后就会失效。
  • LocalStorage是持久性的,在WebView关闭后不会失效,可以进行使用。
//通过webSettings.setDomStorageEnabled(true);//生效。
webSettings.setDomStorageEnabled(true)
IndexDB
  • IndexDB是NoSQL 数据库,通过Key - Value来提供存储功能。
  • 在Android4.4开始对其进行支持
//通过JS执行开发进行使能
webSettings.setJavaScriptEnabled(true);
缓存总结
  1. 存储静态资源文件(JS、CSS、图片资源)- 采用浏览器缓存机制。
  2. 存储临时、简单的数据 - 采用Dom Storage缓存机制。
  3. 存储结构复杂、数据量大的数据 - 采用IndexDB缓存机制。

WebView防止内存泄漏

  • 导致内存泄漏的原因:WebView持有对Activity的引用,导致WebVIew的内存无法释放。
解决方案
  • 使用AIDL跨进程通信,WebView开启一个新的进程,通过AIDL与主进程进行通信,WebView所在的进程在业务合适的阶段对WebView进行销毁。
  • 自己封装WebView,不在XML文件中直接定义,在代码中new出来,并传入Application的context来防止Activity引用导致的泄漏。
activity_webview.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/rl_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
</RelativeLayout>
WebViewActivity.java
public class WebViewActivity extends BaseActivity {

    private RelativeLayout rl_layout;
    private WebView webview;
    private WebSettings webSettings;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_webview);
        initWebView();
    }

    private void initWebView(){
        //避免WebView内存泄露
        rl_layout = (RelativeLayout) findViewById(R.id.rl_layout);
        RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT);
        webview = new WebView(getApplicationContext());
        webview.setLayoutParams(params);
        rl_layout.addView(webview);

        webSettings = webview.getSettings();
        webSettings.setUseWideViewPort(true);//将图片调整到适合webview的大小
        webSettings.setLoadWithOverviewMode(true);// 缩放至屏幕的大小

        webSettings.setSupportZoom(true);//支持缩放,默认为true。是下面那个的前提。
        webSettings.setBuiltInZoomControls(true);//支持缩放,默认为true。是下面那个的前提。
        webSettings.setDisplayZoomControls(false); //隐藏原生的缩放控件

        webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); //关闭webview中缓存
        webSettings.setAllowFileAccess(true); //设置可以访问文件
        webSettings.setJavaScriptCanOpenWindowsAutomatically(true); //支持通过JS打开新窗口
        webSettings.setLoadsImagesAutomatically(true); //支持自动加载图片
        webSettings.setDefaultTextEncodingName("utf-8");//设置编码格式
        webSettings.setDomStorageEnabled(true);// 开启 DOM storage API 功能
        webSettings.setAppCacheEnabled(true);// 开启 Application Caches 功能

        //优先使用缓存:
        // WebView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
        // 缓存模式如下:
        // LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据
        // LOAD_DEFAULT: (默认)根据cache-control决定是否从网络上取数据。
        // LOAD_NO_CACHE: 不使用缓存,只从网络获取数据.
        // LOAD_CACHE_ELSE_NETWORK,只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据。
        // 不使用缓存: WebView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
        //根据以上两种模式,建议缓存策略为:
        // 判断是否有网络,有的话,使用LOAD_DEFAULT,
        // 无网络时,使用LOAD_CACHE_ELSE_NETWORK。
        webview.setWebViewClient(new WebViewClient(){
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                view.loadUrl(url);
                return true;
            }

            //api21以下
            @Override
            public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
                if(url.contains("logo.gif")){
                    InputStream is = null;
                    try {
                        is = getApplicationContext().getAssets().open("images/index.jpg");
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    WebResourceResponse response = new WebResourceResponse("image/jpg","utf-8",is);
                    return response;
                }
                return super.shouldInterceptRequest(view, url);
            }

            //api21以上
            @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/index.jpg");
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    WebResourceResponse response = new WebResourceResponse("image/jpg","utf-8",is);
                    return response;
                }
                return super.shouldInterceptRequest(view, request);
            }

        });

        webview.loadUrl("http://ip.cn/");
    }

    @Override
    protected void onResume() {
        super.onResume();
        webSettings.setJavaScriptEnabled(true);
    }

    @Override
    protected void onPause() {
        super.onPause();
    }

    @Override
    protected void onStop() {
        super.onStop();
        webSettings.setJavaScriptEnabled(false);
    }

    @Override
    protected void onDestroy() {
        //避免WebView内存泄露
        if (webview != null){
            webview.loadDataWithBaseURL(null,"","tex/html","utf-8",null);
            webview.clearHistory();
            ((ViewGroup)webview.getParent()).removeView(webview);
            webview.destroy();
            webview = null;
        }
        super.onDestroy();
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK && webview.canGoBack()){
            webview.goBack();
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }
}

相关文章

网友评论

      本文标题:Android中WebView性能优化

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