YCWebView

Introduction: 基于腾讯 x5 开源库,提高 webView 开发效率,大概要节约你百分之六十的时间成本。该案例支持处理 js 的交互逻辑且无耦合、同时暴露进度条加载进度、可以监听异常 error 状态、支持视频播放并且可以全频、支持加载 word,xls,ppt,pdf,txt 等文件文档、发短信、打电话、发邮件、打开文件操作上传图片、唤起原生 App、x5 库为最新版本,功能强大。
More: Author   ReportBugs   
Tags:

目录介绍

  • 01.前沿说明
    • 1.1 案例展示效果
    • 1.2 该库功能和优势
    • 1.3 相关类介绍说明
  • 02.如何使用
    • 2.1 如何引入
    • 2.2 最简单使用
    • 2.3 常用 api
    • 2.4 使用建议
    • 2.5 异常状态类型区分
    • 2.6 该库流程图
  • 03.js 调用
    • 3.1 Java 调用 js 方法
    • 3.2 js 调用 java 方法
    • 3.3 js 的调用时机分析
    • 3.4 js 交互原理分析
  • 04.问题反馈
  • 05.webView 优化
  • 06.关于参考
  • 07.其他说明介绍

01.前沿说明

  • 基于腾讯 x5 开源库,提高 webView 开发效率,大概要节约你百分之六十的时间成本。该案例支持处理 js 的交互逻辑且无耦合、同时暴露进度条加载进度、可以监听异常 error 状态、支持视频播放并且可以全频、支持加载 word,xls,ppt,pdf,txt 等文件文档、发短信、打电话、发邮件、打开文件操作上传图片、唤起原生 App、x5 库为最新版本,功能强大。

1.1 案例展示效果

  • WebView 启动过程大概分为以下几个阶段,这里借鉴美团的一张图片
    • image
  • 项目案例效果展示图

1.2 该库功能和优势

  • 提高 webView 开发效率,大概要节约你百分之六十的时间成本,一键初始化操作;
  • 支持处理 js 的交互逻辑,方便快捷,并且无耦合,操作十分简单;
  • 暴露进度条加载进度,结束,以及异常状态(分多种状态:无网络,404,onReceivedError,sslError 异常等)listener 给开发者;
  • 支持视频播放,可以切换成全频播放视频,可旋转屏幕,暴露视频操作监听 listener 给开发者;
  • 集成了腾讯 x5 的 WebView,最新版本,功能强大;
  • 支持打开文件的操作,比如打开相册,然后选中图片上传,兼容版本(5.0);
  • 支持加载 word,xls,ppt,pdf,txt 等文件文档,使用方法十分简单;
  • 支持设置仿微信加载 H5 页面进度条,完全无耦合,操作简单,极大提高用户体验;
  • 支持用户按照规范自定义 WebViewClient 和 WebChromeClient,不影响 js 通信;

1.3 相关类介绍说明

  • BridgeHandler 接口,主要处理消息回调逻辑
  • BridgeUtil 工具类,静态常量,以及获取 js 消息的一些方法,final 修饰
  • BridgeWebView 自定义 WebView 类,主要处理与 js 之间的消息
  • CallBackFunction js 回调
  • DefaultHandler 默认的 BridgeHandler
  • InterWebListener 接口,web 的接口回调,包括常见状态页面切换【状态页面切换】,进度条变化【显示和进度监听】等
  • Message 自定义消息 Message 实体类
  • ProgressWebView 自定义带进度条的 webView
  • WebViewJavascriptBridge js 桥接接口
  • X5WebChromeClient 自定义 x5 的 WebChromeClient,处理进度监听,title 变化,以及上传图片,后期添加视频处理逻辑
  • X5WebUtils 工具类,初始化腾讯 x5 浏览器 webView,及调用该类 init 方法
  • X5WebView 可以使用这个类,方便统一初始化 WebSettings 的一些属性,如果不用这里的,想单独初始化 setting 属性,也可以直接使用 BridgeWebView
  • X5WebViewClient 自定义 x5 的 WebViewClient,如果要自定义 WebViewClient 必须要集成此类,一定要继承该类,因为注入 js 监听是在该类中操作的

1.4 WebView 基础知识点

  • 01.常用的基础介绍
  • 02.Android 调用 Js
  • 03.Js 调用 Android
  • 04.WebView.loadUrl(url)流程
  • 05.js 的调用时机分析
  • 06.清除缓存数据方式有哪些
  • 07.如何使用 DeepLink
  • 08.为什么 WebView 那么难搞
  • 09.如何处理加载错误
  • 10.应用被作为第三方浏览器打开
  • 11.理解 WebView 独立进程
  • 更多内容看 wiki

02.如何使用

2.1 如何引入

  • 如何引用,该 x5 的库已经更新到最新版本
      implementation 'cn.yc:WebViewLib:1.1.6'
    

2.2 最简单使用

  • 项目初始化
      X5WebUtils.init(this);
    
  • 最普通使用,需要自己做手动设置 setting 相关属性
      <BridgeWebView
          android:id="@+id/web_view"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:scrollbarSize="3dp" />
    
  • 也可以使用 X5WebView,已经做了常见的 setting 属性设置
      <X5WebView
          android:id="@+id/web_view"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:scrollbarSize="3dp" />
    
  • 如果想有带进度的,可以使用 ProgressWebView
      <可以使用 ProgressWebView
          android:id="@+id/web_view"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:scrollbarSize="3dp" />
    
  • 如何使用自己的 WebViewClient 和 WebChromeClient

      //主要是在 X5WebViewClient 和 X5WebChromeClient 已经做了很多常见的逻辑处理,如果不满足你使用,可以如下这样写
      MyX5WebViewClient webViewClient = new MyX5WebViewClient(webView, this);
      webView.setWebViewClient(webViewClient);
      MyX5WebChromeClient webChromeClient = new MyX5WebChromeClient(this);
      webView.setWebChromeClient(webChromeClient);
    
      private class MyX5WebViewClient extends X5WebViewClient {
          public MyX5WebViewClient(BridgeWebView webView, Context context) {
              super(webView, context);
          }
    
          //重写你需要的方法即可
      }
    
      private class MyX5WebChromeClient extends X5WebChromeClient{
          public MyX5WebChromeClient(Activity activity) {
              super(activity);
          }
    
          //重写你需要的方法即可
      }
    

2.3 常用 api

  • 关于 web 的接口回调,包括常见状态页面切换,进度条变化等监听处理

      mWebView.getX5WebChromeClient().setWebListener(interWebListener);
      private InterWebListener interWebListener = new InterWebListener() {
          @Override
          public void hindProgressBar() {
              pb.setVisibility(View.GONE);
          }
    
          @Override
          public void showErrorView(@X5WebUtils.ErrorType int type) {
              //设置自定义异常错误页面
          }
    
          @Override
          public void startProgress(int newProgress) {
              //该方法是是监听进度条进度变化的逻辑
              pb.setProgress(newProgress);
          }
      };
    
  • 关于视频播放的时候,web 的接口回调,主要是视频相关回调,比如全频,取消全频,隐藏和现实 webView

      x5WebChromeClient = x5WebView.getX5WebChromeClient();
      x5WebChromeClient.setVideoWebListener(new VideoWebListener() {
          @Override
          public void showVideoFullView() {
              //视频全频播放时监听
          }
    
          @Override
          public void hindVideoFullView() {
              //隐藏全频播放,也就是正常播放视频
          }
    
          @Override
          public void showWebView() {
              //显示 webView
          }
    
          @Override
          public void hindWebView() {
              //隐藏 webView
          }
      });
    
  • 其他 api 说明
      //X5WebView 中
      //设置是否开启密码保存功能,不建议开启,默认已经做了处理,存在盗取密码的危险
      mWebView.setSavePassword(false);
      //是否开启软硬件加速
      mWebView.setOpenLayerType(false);
      //获取 x5WebChromeClient 对象
      x5WebChromeClient = mWebView.getX5WebChromeClient();
      //获取 x5WebViewClient 对象
      x5WebViewClient = mWebView.getX5WebViewClient();
    
  • 关于如何使用仿微信加载 H5 页面进度条
    • 前端页面时受到网路环境,页面内容大小的影响有时候会让用户等待很久。显示一个加载进度条可以说很大程度上提升用户的体验。
      private WebProgress pb;
      //显示进度条
      pb.show();
      //设置进度条过度颜色
      pb.setColor(Color.BLUE,Color.RED);
      //设置单色进度条
      pb.setColor(Color.BLUE);
      //为单独处理 WebView 进度条
      pb.setWebProgress(newProgress);
      //进度完成后消失
      pb.hide();
      
  • 设置 cookie 和清除 cookie 操作
      //同步 cookie
      X5WebUtils.syncCookie(this,"url",cookieList);
      //清除 cookie
      X5WebUtils.removeCookie(this);
    

2.4 使用建议

  • 优化一下相关的操作

    • 关于设置 js 支持的属性

      @Override
      public void onResume() {
        super.onResume();
        if (mWebView != null) {
            mWebView.getSettings().setJavaScriptEnabled(true);
        }
      }
      
      @Override
      protected void onStop() {
        super.onStop();
        if (mWebView != null) {
            mWebView.getSettings().setJavaScriptEnabled(false);
        }
      }
      
    • 关于 destroy 销毁逻辑
      @Override
      protected void onDestroy() {
        try {
            if (webView != null) {
                webView.stopLoading();
                webView.destroy();
                webView = null;
            }
        } catch (Exception e) {
            Log.e("X5WebViewActivity", e.getMessage());
        }
        super.onDestroy();
      }
      

2.5 关于 web 页面异常状态区分类型

  • 对于 web 加载异常,分为多种状态,比如常见的有,没有网络;404 加载异常;onReceivedError,请求网络出现 error;在加载资源时通知主机应用程序发生 SSL 错误
      @Override
      public void showErrorView(@X5WebUtils.ErrorType int type) {
          switch (type){
              //没有网络
              case X5WebUtils.ErrorMode.NO_NET:
                  break;
              //404,网页无法打开
              case X5WebUtils.ErrorMode.STATE_404:
                  break;
              //onReceivedError,请求网络出现 error
              case X5WebUtils.ErrorMode.RECEIVED_ERROR:
                  break;
              //在加载资源时通知主机应用程序发生 SSL 错误
              case X5WebUtils.ErrorMode.SSL_ERROR:
                  break;
              default:
                  break;
          }
      }
    

2.6 该库流程图

  • java 调用 js 的流程图
    • 第一步操作:mWebView.callHandler("functionInJs", "小杨逗比", new CallBackFunction() {//这里面是回调});
    • 第二步操作:将 handlerName,data,responseCallback,封装到 Message 对象中,然后开始分发数据,最后 webView 执行 _handleMessageFromNative;
    • 第三步操作:去 WebViewJavascriptBridge.js 类中找到 _handleMessageFromNative 方法,js 根据"functionInJs"找到对应的 js 方法并且执行;
    • 第四步操作:js 把运行结果保存到 message 对象中,然后添加到 js 消息队列中;
    • 第五步操作:在 _dispatchMessageFromNative 方法中,可以看到,js 向 native 发送 "消息队列中有消息" 的通知;
    • 第六步操作:webView 执行 js 的 _fetchQueue(WebViewJavascriptBridge.js 类)方法;
    • 第七步操作:js 把消息队列中的所有消息都一起回传给 webView;
    • 第八步操作:webView 收到所有的消息,一个一个串行处理,注意其中包括 "functionInJs"方法运行的结果的消息;
  • js 调用 Android 的流程图
    • 第一步操作:mWebView.registerHandler("toPhone", new BridgeHandler() { //回调});
    • 第二步操作:调用 messageHandlers.put(handlerName, handler),将名称和 BridgeHandler 对象放到 map 集合中
    • 第三步操作:在 shouldOverrideUrlLoading 方法中拦截 url,与网页约定好一个协议,匹配则执行相应操作,也就是利用 WebViewClient 接口回调方法拦截 url
    • 第四步操作:如果是 url.startsWith(BridgeUtil.YY_RETURN_DATA)则有数据返回;如果是 BridgeUtil.YY_OVERRIDE_SCHEMA 则刷新消息队列
    • 第五步操作:通过 BridgeHandler 对象,将 data 和 callBackFunction 返回交给开发者

03.js 交互操作

3.1 Java 调用 js 的使用方法

  • 代码如下所示,下面 updateAttentionStatus 代表 js 这边的方法名称

    • webView.callHandler(“updateAttentionStatus”, …, new CallBackFunction());这是 Java 层主动调用 Js 的”updateAttentionStatus”方法。

      mWebView.callHandler("updateAttentionStatus", attention, new CallBackFunction() {
        @Override
        public void onCallBack(String data) {
      
        }
      });
      

3.2 js 调用 java 的使用方法

  • 代码如下所示,下面中的 toPhone 代表的是 Android 这边提供给 js 的方法名称
    • webView.registerHandler(“toPhone”, …);这是 Java 层注册了一个叫”toPhone”的接口方法,目的是提供给 Js 来调用。这个”toPhone”的接口方法的回调就是 BridgeHandler.handler()。
      mWebView.registerHandler("toPhone", new BridgeHandler() {
        @Override
        public void handler(String data, CallBackFunction function) {
            try {
                JSONObject jsonData = new JSONObject(data);
                String phone = jsonData.optString("phone");
                //todo 打电话
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
      });
      
  • 如何回调数据给 web 那边
      //注意,这里回传数据目前只是支持 String 字符串类型
      function.onCallBack("回调数据");
    

3.3 js 的调用时机分析

  • onPageFinished()或者 onPageStarted()方法中注入 js 代码
    • 做过 WebView 开发,并且需要和 js 交互,大部分都会认为 js 在 WebViewClient.onPageFinished()方法中注入最合适,此时 dom 树已经构建完成,页面已经完全展现出来。但如果做过页面加载速度的测试,会发现 WebViewClient.onPageFinished()方法通常需要等待很久才会回调(首次加载通常超过 3s),这是因为 WebView 需要加载完一个网页里主文档和所有的资源才会回调这个方法。
    • 能不能在 WebViewClient.onPageStarted()中注入呢?答案是不确定。经过测试,有些机型可以,有些机型不行。在 WebViewClient.onPageStarted()中注入还有一个致命的问题——这个方法可能会回调多次,会造成 js 代码的多次注入。
    • 从 7.0 开始,WebView 加载 js 方式发生了一些小改变,官方建议把 js 注入的时机放在页面开始加载之后
  • WebViewClient.onProgressChanged()方法中注入 js 代码
    • WebViewClient.onProgressChanged()这个方法在 dom 树渲染的过程中会回调多次,每次都会告诉我们当前加载的进度。
      • 在这个方法中,可以给 WebView 自定义进度条,类似微信加载网页时的那种进度条
      • 如果在此方法中注入 js 代码,则需要避免重复注入,需要增强逻辑。可以定义一个 boolean 值变量控制注入时机
    • 那么有人会问,加载到多少才需要处理 js 注入逻辑呢?
      • 正是因为这个原因,页面的进度加载到 80%的时候,实际上 dom 树已经渲染得差不多了,表明 WebView 已经解析了标签,这时候注入一定是成功的。在 WebViewClient.onProgressChanged()实现 js 注入有几个需要注意的地方:
      • 1 上文提到的多次注入控制,使用了 boolean 值变量控制
      • 2 重新加载一个 URL 之前,需要重置 boolean 值变量,让重新加载后的页面再次注入 js
      • 3 如果做过本地 js,css 等缓存,则先判断本地是否存在,若存在则加载本地,否则加载网络 js
      • 4 注入的进度阈值可以自由定制,理论上 10%-100%都是合理的,不过建议使用了 75%到 90%之间可以。

3.4 js 交互原理分析

  • 01.WebView 加载 html 页面
  • 02.加载 WebViewJavascriptBridge.js
  • 03.分析 WebViewJavascriptBridge.js
  • 04.页面 Html 注册”functionInJs”方法
  • 05.“functionInJs”执行结果回传 Java
  • 更多内容看 wiki

04.问题反馈

  • 4.0.0 WebView 进化史介绍
  • 4.0.1 提前初始化 WebView 必要性
  • 4.0.2 x5 加载 office 资源
  • 4.0.3 WebView 播放视频问题
  • 4.0.4 无法获取 webView 的正确高度
  • 4.0.5 使用 scheme 协议打开链接风险
  • 4.0.6 如何处理加载错误
  • 4.0.7 webView 防止内存泄漏
  • 4.0.8 关于 js 注入时机修改
  • 4.0.9 视频播放宽度超过屏幕
  • 4.1.0 如何保证 js 安全性
  • 4.1.1 如何代码开启硬件加速
  • 4.1.2 WebView 设置 Cookie
  • 4.1.4 webView 加载网页不显示图片
  • 4.1.5 绕过证书校验漏洞
  • 4.1.6 allowFileAccess 漏洞
  • 4.1.7 WebView 嵌套 ScrollView 问题
  • 4.1.8 WebView 中图片点击放大
  • 4.1.9 页面滑动期间不渲染/执行
  • 4.2.0 被运营商劫持和注入问题
  • 4.2.1 解决资源加载缓慢问题
  • 4.2.2 判断是否已经滚动到页面底端
  • 4.2.3 使用 loadData 加载 html 乱码
  • 4.2.4 WebView 下载进度无法监听
  • 4.2.5 webView 出现 302/303 重定向
  • 4.2.6 webView 出现 302/303 白屏
  • 4.2.8 onReceiveError 问题
  • 4.2.9 loadUrl 在 19 以上超过 2097152 个字符失效
  • 4.3.0 WebViewJavascriptBridge: WARNING
  • 4.3.1 Android 与 js 传递数据大小有限制
  • 4.3.2 多次调用 callHandler 部分回调函数未被调用
  • 4.3.3 字符串转义 bug 探讨
  • 4.3.8 Javascript 调用原生方法会偶现失败
  • 4.3.9 dispatchMessage 运行主线程问题
  • 4.4.0 怎么实现 WebView 免流方案
  • 4.4.1 Channel is unrecoverably broken and will be disposed!
  • 4.4.2 定制 js 的 alert,confirm 和 prompt 对话框
  • 4.4.3 x5 长按图片如何操作
  • 4.4 4 x5 长按文字内容如何自定义弹窗
  • 更多问题反馈内容

05.webView 优化

  • 5.0.1 视频全屏播放按返回页面被放大
  • 5.0.2 加快加载 webView 中的图片资源
  • 5.0.3 自定义加载异常 error 的状态页面
  • 5.0.4 WebView 硬件加速导致页面渲染闪烁
  • 5.0.5 WebView 加载证书错误
  • 5.0.6 web 音频播放销毁后还有声音
  • 5.0.7 DNS 采用和客户端 API 相同的域名
  • 5.0.8 如何设置白名单操作
  • 5.0.9 后台无法释放 js 导致发热耗电
  • 5.1.0 可以提前显示加载进度条
  • 5.1.1 WebView 密码明文存储漏洞优化
  • 5.1.2 页面关闭后不要执行 web 中 js
  • 5.1.3 WebView + HttpDns 优化
  • 更多 webView 优化内容

06.关于参考

07.其他说明介绍

关于其他内容介绍

image

关于博客汇总链接

其他推荐

  • 博客笔记大汇总【15 年 10 月到至今】,包括 Java 基础及深入知识点,Android 技术博客,Python 学习笔记等等,还包括平时开发中遇到的 bug 汇总,当然也在工作之余收集了大量的面试题,长期更新维护并且修正,持续完善……开源的文件是 markdown 格式的!同时也开源了生活博客,从 12 年起,积累共计 47 篇[近 100 万字],转载请注明出处,谢谢!
  • 链接地址:https://github.com/yangchong211/YCBlogs
  • 如果觉得好,可以 star 一下,谢谢!当然也欢迎提出建议,万事起于忽微,量变引起质变!

关于 LICENSE

Copyright 2017 yangchong211(github.com/yangchong211)

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Apps
About Me
Google+: Trinea trinea
GitHub: Trinea