专栏首页lib库WebView开源库终极方案
原创

WebView开源库终极方案

目录介绍

  • 01.前沿说明
    • 1.1 案例展示效果
    • 1.2 该库功能和优势
    • 1.3 相关类介绍说明
    • 1.4 WebView知识点
  • 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库为最新版本,功能强大。项目地址:webView开源库
  • 同时,该案例中,04问题反馈也记录了绝大多数实际开发中遇到的问题,如果还有其他的问题,也可以发送给我,我收集起来放到一起,方便后期查阅。
  • 可以说,该开源库,以及附带的webView基础知识点,问题汇总,以及优化方案等笔记,相对来说比较系统而又全面的梳理webView的知识体系,相对而言也是比较实用的,也不乏参考了一些好的案例和博客文章。

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通信;
  • 汇集绝大多数问题,以及解决方案,是学习和深入理解webView的一个比较全面的案例;

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监听是在该类中操作的
  • WebProgress 仿微信加载H5页面的WebView进度条

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独立进程
  • 12.使用外部浏览器下载
  • 13.tel,sms等协议用法
  • 更多内容看wiki

02.如何使用

2.1 如何引入

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

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);
    }
    //重写你需要的方法即可
}
```
  • 针对类似购物的商品详情页面的webView
    • 当WebView在最顶部或者最底部的时候,不消费事件,则可以使用VerticalWebView

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);
    }
    @Override
    public void showTitle(String title) {
        //该方法是监听h5中title
    }
};
```
  • 关于视频播放的时候,web的接口回调,主要是视频相关回调,比如全频,取消全频,隐藏和现实webViewx5WebChromeClient = 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已经解析了<html>标签,这时候注入一定是成功的。在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.3 开启硬件加速导致的闪烁问题
  • 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长按文字内容如何自定义弹窗
  • 4.4.5 webView.goBack()会刷新页面吗
  • 4.4.6 mWebView.scrollTo(0, 0)回顶部失效
  • 4.4.7 部分手机监听滑动顶部或底部失效
  • 4.4.8 prompt的一个坑导致js挂掉
  • 4.4.9 webView背景设置为透明无效探索
  • 4.5.0 如何屏蔽掉WebView中长按事件
  • 4.5.1 WeView出现OOM影响主进程如何避免
  • 4.5.2 WebView域控制不严格漏洞
  • 4.5.3 下载文件时的路径穿越问题
  • 4.5.4 WebView中http和https混合使用问题
  • 4.5.5 调用系统EMAIL发送邮件崩溃
  • 4.5.7 WebView访问部分网页崩溃问题
  • 更多问题反馈内容

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优化
  • 5.1.4 如何禁止WebView返回时刷新
  • 5.1.5 WebView处理404、500逻辑
  • 5.1.6 WebView判断断网和链接超时
  • 5.1.7 @JavascriptInterface注解方法注意点
  • 5.1.8 使用onJsPrompt实现js通信注意点
  • 5.1.9 Cookie同步场景和具体操作
  • 5.2.0 shouldOverrideUrlLoading处理多类型
  • 更多webView优化内容

06.关于参考

07.其他说明介绍

关于博客汇总链接

其他推荐

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

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • OpenRASP梳理总结

    RASP英文为 Runtime application self-protection,即运行时应用程序自我保护。“运行时应用程序自我保护”的概念由Gartne...

    FB客服
  • 一份不可多得的 Lombok 学习指南

    Lombok 是一款 Java 开发插件,使得 Java 开发者可以通过其定义的一些注解来消除业务工程中冗长和繁琐的代码,尤其对于简单的 Java 模型对象(P...

    江南一点雨
  • [译]JS 模块化历史简介

    对于 JavaScript 来说,模块化是一个相对现代的概念,这篇文章会带你在 JavaScript 的世界里快速浏览模块化的历史进程~

    savokiss
  • Apache Solr最新RCE漏洞分析

    Apache Solr爆出RCE 0day漏洞(漏洞编号未给出),这里简单的复现了对象,对整个RCE的流程做了一下分析,供各位看官参考。

    FB客服
  • Spring周边:国际化、Format

    Locale 类代表一个特定的地理、语言和国家环境。Locale 向一些对国家和语言、地理等比较敏感的类提供国家地区语言信息,这些类有DateFormat、Nu...

    WEBJ2EE
  • Android | 如何使程序实现线程安全(拓展关键词:ThreadLocal、重排序、volatile/final)

    假设Thread1 为 writer线程,初始化了一个FinalFieldExample实例f, Thread2 为 reader线程,读取实例f 的x、y值...

    凌川江雪
  • 5 门正在奋力崛起的编程语言

    在软件项目与具体实现层面,我们需要考虑众多具体因素。但无论从哪种角度出发,技术栈的选择永远是决定项目成功与否的核心因素之一。根据您的实际应用需求、站点或者产品设...

    GitHubDaily
  • 面试必问:找出一组数中最小的K个数(海量数据Top K问题)

    输入 n 个整数,找出其中最小的 k 个数。例如输入4、5、1、6、2、7、3、8 这8个数字,则最小的4个数字是1、2、3、4。

    乔戈里
  • 从Reactor到WebFlux

    为了应对高并发场景下到服务端编程需求,微软最先提出了一种异步编程到方案Reactive Programming,也就是反应式编程。

    春哥大魔王
  • Spring周边:Formatter(字符串格式化)

    Formatter 通过将程序使用的数据的二进制形式转换成格式化的文本进行工作,这个格式化动作在“缓冲区”中进行,可以让 Formatter 自动提供这个缓冲区...

    WEBJ2EE

扫码关注云+社区

领取腾讯云代金券