「一网打尽」WebView 秘籍全解析

·  阅读 975

WebView 相关概念

一、背景

学习一个东西,首先得知道为什么学习它,其次才是它的一些使用方法。那么为什么要使用 WebView 呢?我们知道像电商这类的 App 经常会有一些活动啥的,如果使用原生的 Android 开发,既耗时又耗力,App 更新频繁。但是如果使用 WebView 的话,只需要更改 HTML 页面即可,极大地节省了成本。

二、简介

WebView 是一个基于 webkit 引擎、展现 web 页面的控件。

WebView 的作用如下:

  • 在 App 中显示和渲染 Web 页面。
  • 直接使用 HTML 作为布局。
  • 可以与 JS 进行交互。

WebView 使用详解

WebView 既可以自己单独使用,也可以联合其工具类一起使用,下面主要分为三个部分来进行介绍:

  • WebView 自身常见的方法。
  • WebView 组合使用的的工具类:WebSettings、WebViewClient、WebChromeClient
  • Android 和 JS 的交互。

一、WebView 自身常见的方法

  • 加载 URL

    //从网页加载
    webview.loadUrl("https://google.com")
    //加载 apk 下的 html
    webview.loadUrl("file://android_asset/test.html")
    //加载手机本地的 html 
    webview.loadUrl("content://com.android.htmlfileprovider/sdcard/test.html")
    复制代码
  • WebView 的状态

    //激活 WebView 的状态,可以执行网页的响应
    webview.onResume()
    //失去焦点不可见,通知内核暂停所有的行动
    webview.onPause()
    //暂停所有 WebView 的行动,降低消耗
    webview.pauseTimer()
    //恢复行动
    webview.resumeTimers()
    复制代码
  • 关于 前进/后退 网页

    //是否可以后退
    Webview.canGoBack() 
    //后退网页
    Webview.goBack()
    
    //是否可以前进                     
    Webview.canGoForward()
    //前进网页
    Webview.goForward()
    
    //以当前的index为起始点前进或者后退到历史记录中指定的steps
    //如果steps为负数则为后退,正数则为前进
    Webview.goBackOrForward(intsteps) 
    复制代码
  • 解决 Back 键 Finish Browser

    //按手机的 back 会 finish() 自身而结束,下面的代码解决这个问题
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if ((keyCode == KEYCODE_BACK) && mWebView.canGoBack()) { 
            mWebView.goBack();
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }
    复制代码

二、WebView 组合使用的工具类

1、WebSettings 类

  • 创建 WebView 的两种方式

    // 1、直接在 Activity 中生成
    WebView webview = new WebView(this)
    // 2、在 XML 布局中定义,在 Activity 获取对象
    WebView webview = findViewById(R.id.webview)
    复制代码
  • 利用 WebSettings 进行配置

    //声明WebSettings子类
    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); //隐藏原生的缩放控件
    
    //其他细节操作
    // 只要本地有,无论是否过期,或者 no-cache,都使用缓存中的数据
    webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);  
    // 不使用缓存,只从网络中获取数据
    webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);
    // (默认)根据 cache-control 或者 Last-Modified 决定是否从网络中获取数据
    webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);
    // 不使用网络,只读取本地缓存数据
    webSettings.setCacheMode(WebSettings.LOAD_CACHE_ONLY);
    webSettings.setAllowFileAccess(true); //设置可以访问文件 
    webSettings.setJavaScriptCanOpenWindowsAutomatically(true); //支持通过JS打开新窗口 
    webSettings.setLoadsImagesAutomatically(true); //支持自动加载图片
    webSettings.setDefaultTextEncodingName("utf-8");//设置编码格式	
    复制代码

2、WebViewClient 类

  • 作用: 处理各种通知 & 请求事件

  • 常用方法:

    webview.setWebViewClient(new WebViewClient(){
    	@Override
    	public boolean shouldOverrideUrlLoading(View view,String url){
    			view.loadUrl(url);
    	}
    });
    // 打开网页是不调用浏览器,而是直接在本 WebView 中显示
    shouldOverrideUrlLoading()
    // 开始载入页面时进行调用
    onPageStarted()
    // 页面加载结束时调用
    onPageFinish()
    // 在页面加载资源时调用,每一资源都会调用一次
    onLoadResource()
    // 加载页面的服务器出现错误时调用
    onReceivedError()
    复制代码

3、WebChromeClient 类

  • 作用: 辅助 WebView 处理 JS 的对话框、网站图标、网站标题等。

  • 常用方法:

    // 获取网页的加载进度并进行显示
    onProgressChanged()
    // 获取 Web 页的标题
    onReceivedTitle()
    // 支持 JS 的警告框
    onJsAlter()
    //.....
    复制代码

三、WebView 和 JavaScript 交互

  • Android 调用 JS 代码总结
    • 通过 WebView 的 loadUrl()。
    • 通过 WebView 的 evaluateJavaScript()。
  • JS 调用 Android 代码总结
    • 通过调用 WebView 的 addJavascriptInterface() 进行对象映射。
    • 通过 WebViewClient 的 onJsAlter()、onJsConfirm()、onJsPrompt()方法回调拦截 JS 对话框 alter()、confirm()、prompt()

三、WebView 内存泄漏问题

1、如何避免内存泄漏

  • 不在 XML 中定义 WebView,而在是需要的时候在 Activity 创建,并且Context 使用 getApplicationContext()

    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
            mWebView = new WebView(getApplicationContext());
            mWebView.setLayoutParams(params);
            mLayout.addView(mWebView);
    复制代码
  • 在 Activity 销毁的时候,先让 WebView 加载 null,然后在移除 WebView,最后置空。

    @Override
        protected void onDestroy() {
            if (mWebView != null) {
              	//先加载 null 内容
                mWebView.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);
                mWebView.clearHistory();
    						//移除 WebView
                ((ViewGroup) mWebView.getParent()).removeView(mWebView);
              	//销毁
                mWebView.destroy();
              	//置空
                mWebView = null;
            }
            super.onDestroy();
        }
    复制代码

2、探究内存泄漏的原因(TODO)

​ 这是由 WebView 的内核引起的,一般在低版本的 Android 才会出现,高版本已经进行修复。原因具体如下:

org.chromium.android_webview.AwContents 类中注册了component callbacks,但是未正常反注册而导致的。 org.chromium.android_webview.AwContents 类中有这两个方法 onAttachedToWindow 和 onDetachedFromWindow; 系统会在attach和detach处进行注册和反注册 component callback

​ 相关文章参考:juejin.cn/post/690148…

四、WebView 相关的性能优化

WebView 给人的感觉就是它的加载速度很慢,比原生的 Native 慢了很多,往往加载一个页面需要耗费大量时间。这就需要我们对 WebView 进行相关的优化,首先得了解 WebView 的启动过程:

9a2f8beb

1、WebView 初始化

只有当 WebView 都创建并初始化完成以后,才开始加载页面的资源,那么怎样才能解决这样的情况。有下面几种常用的解决方案:

  • 获取全局 的 Context

    减少 WebView 在 App 首次打开时初始化的时间,但是增加了额外的内存消耗。同时,页面间进行跳转需要清除上一个页面留存的痕迹,更加容易引起内存泄漏。

  • 客户端代理数据请求

    • 在客户端初始化 WebView 的同时,直接由native开始网络请求数据;
    • 当页面初始化完成后,向 native 获取其代理请求的数据。

    此方法虽然不能减少 WebView 的初始化时间,但是数据请求和 WebView 初始化同时进行,总体的页面加载时间缩短了。

2、建立连接/服务器处理

  • DNS 采用和客户端 API 相同的域名

    客户端首次打开会对一个域名发起请求,其 DNS 将会被缓存,当打开 WebView 的时候,又会对一个新的 Api 进行请求,就会重新发起 DNS 请求,增加了时间的消耗。

  • 同步渲染采用 chunk 编码

    同步渲染时如果后端请求时间过长,可以考虑采用 chunk 编码,将数据放在最后,优先将静态内容加载出来。对于传统的后段渲染,往往都是采用【浏览器】-> 【Web API】-> 【业务 API】的加载模式,其中后段时间就是指【Web API】的处理时间。这里的 Web API 一般有两个作用:

    • 确定静态资源的版本。
    • 根据用户的请求,去业务 API 获取数据。

    而确定静态资源的版本基本上是无耗时的,后端请求时间基本上花费在了业务 API 的请求上。那么如何对这段时间进行优化呢?我们可以在header中设置 transfer-encoding:chunked 使得页面可以分块输出,如果合理设计页面,让head部分都是确定的静态资源版本相关内容,而body部分是业务数据相关内容,那么我们可以在用户请求的时候,首先将Web API可以确定的部分先输出给浏览器,然后等API完全获取后,再将API数据传输给浏览器。由下图可以看清楚 chunk 分块请求和 完全输出的区别:

4c33eae7

3、页面框架渲染

通常情况下,前端的的 CSS 加载 和 JS 脚本的执行都不会阻塞页面的解析,但是如果同时出现的顺序不当,则会阻塞页面的解析,执行情况是这样的:

  • CSS 不会阻止页面继续向下解析。
  • 内联的 JS 会很快执行完成,然后继续解析文档。
  • 但是 CSS 的加载会阻塞后面 JS 的执行,从而间接阻塞 HTML 的解析。

参考文章:

www.jianshu.com/p/3c94ae673…

www.jianshu.com/p/345f4d8a5…

www.jianshu.com/p/3e8f7dbb0…

juejin.cn/post/690148…

分类:
Android
标签:
分类:
Android
标签:
收藏成功!
已添加到「」, 点击更改