Hybrid 应用中 H5 与 Native 通信的那点事儿

作者:董华磊,百度高级研发工程师

APP 开发技术对比

  • Native App

  • 一般是指本地化应用,后续简称 NA;

  • 优点:体验好,可以做一些比较好的交互效果,可作为独立软件出售;

  • 缺点:更新较差,需要靠发版本解决;且历史版本无法同步更新,开发成本比较大,需要两波开发人员:Android 和 IOS,分别使用 Object-c 和 Java;

  • Web App

  • 一般是指我们开发的Html5网站,后续简称 H5;

  • 优点:开发成本较低,前端开发人员开发一套同时适配 IOS 和 Android;更新好,可随时上线,上线后版本能普及到所有使用的用户;

  • 缺点:体验没有 NA 好,没有独立的软件作为入口(当然后续如果 PWA 能普及并支持下载,这也不再是缺点了);

  • Hybird App

  • 一般是指混合型 App,一部分是 NA 开发人员开发,一部分是 H5 页面;

  • 优点:跨平台开发周期短、成本低,又能发挥Native App体验和性能的优势

  • 缺点:涉及到 H5 与 NA 通信,需要客户端开发和前端开发协同合作;

  • 其他 App

  • js + 原生渲染,框架代表:RN、Weex;自绘 UI + 原生,框架代表:Flutter,对此的介绍可参考移动开发技术简介

Hybrid App 应用简介

Hybrid App 是半原生半 web 的开发模式,部分界面或者功能是原生的,其余部分是 html 和 js 来完成,最终 webview 加载 H5 页面;入职百度后第一个做的成型的产品是 Hybrid App,在开发过程中研究了 H5 与 NA 通信机制,特对通信机制进行介绍。

举例

在安卓中可以通过开启”设置“->”开发者选项“->”显示布局边界“,所有 NA 部分都会被框选出来,例如百度 App 中的首页,所有部分都有框选,整个页面都为 NA 实现;如下图所示;


再比如百度 App 中的搜索结果页,上面搜索框的部分和下面的 Bar 都是 NA 的,而中间嵌入的刘德华页面为 H5 页面,如下图所示;再比如飞猪的机票预定页等



这种页面比比皆是,采用 H5 和 NA 结合 即 Hybrid App 的形式呈现。

通信

以我接触到的通信方式分别进行解析;

1、NA 和 H5 分别将方法挂载在 window 上,供对方使用
H5 调用 NA

NA 页面将方法注入到 window 上,H5 直接调用

webview.addjs + 原生渲染,框架代表:RN、WeexInterface(new Object, 'BdTing');

在 Object 中定义 play 方法;

H5 调用

window.BdTing.play();  // 播放
NA 调用 H5

H5 将方法挂载在 window 上

window.BdTing.share = function () {};

NA 调用 H5 的方法

webview.loadUrl("js + 原生渲染,框架代表:RN、Weex:share()");

注:addjs + 原生渲染,框架代表:RN、WeexInterface 在安卓4.2以下存在安全漏洞;

2、NA 和 H5 通过 JsBridge 桥梁进行通信

原理:基于 iframe 修改 src 值,NA 可以捕获 url 变化,根据 url 变化,判断 H5 的意图,大体通信过程如下图所示;


在启动 App 时,App 会在 webview 中执行一个 js 文件,这个文件在 NA 与 H5 中间建立了一个桥梁,NA 与 H5 基于这个桥梁通信,这个 js 文件的作用如下图所示;具体的 js 文件见JsBridge

  • 创建一个隐藏的iframe,iframe 的作用是在 H5 与 NA 通信时,会修改 iframe 的 src 值,src的变化会触发 shouldOverriderUrlLoading 方法,这个方法的第二个参数为 src 值,方法内解析 src 值判断 H5 的意图;

  • 在 webview 中的 window 上挂载 JsBridge 对象,这个对象中包含了一些方法:
  • H5 调用 NA 的方法,参数:事件名称、参数、回调函数
  • callHandler 方法会修改 iframe 的 src 值,NA 捕获到 src 变化,解析 src,判断 H5 意图;
  • 根据数据和事件,NA 做相应的处理,处理完成后,调用回调函数,同时把想回传给 H5 的数据作为回调函数的参数传递;
  • 注册方法,在 NA 状态变化时执行,参数:方法名,回调函数
  • registerHandler 会将方法名对应的回调函数挂载在messageHandlers,即messageHandlers[methodname] = callback;
  • NA 状态发生变化,想通知 H5,则调用相应的挂载在 messageHandlers 上的方法;
  • registerHandler方法
  • callHandler方法

注:如果这两个方法比较抽象,可以看一下下面的流程详解,再回过来看当前的说明;

H5 调用 NA 详细流程


我们以音频播放举例:由于希望用户离开 H5 页面时仍然可以播放音频的要求,我们采用 Hybrid App 的方案,其中音频列表是 H5 实现,播放器是 NA 实现。当用户点击音频列表中的一项时,触发的 H5 和 NA 交互流程如下:

  1. H5 通知 NA 需要播放的音频相关参数以及回调函数,即调用 JsBridge 对象的 callHandler 方法;


2. callHandler 内会修改 iframe 的 src 值为:yy:__QUEUE_HAS_MESSAGE,同时将 H5 的回调函数和相关参数 push 到消息队列;


3. src 值的变化被 NA 捕获,NA 捕获后解析 src 值,根据 src 的 yy:__QUEUE_HAS_MESSAGE 判断 H5 有消息通知到自己,NA 会调用 JsBridge 的_fetchQueue 方法


4. _fetchQueue 方法会再次修改 iframe 的 src 值,将参数和调用的方法名统一添加到 src 上;NA 捕获到 src 变化后,会解析出音频参数和方法名:play,同时打开 NA 的播放器页面并播放相应的音频,并调用 JsBridge 的 _handleMessageFromNative
5. _handleMessageFromNative 方法内将调用初始消息队列保存的回调函数,并将 NA 要通知 H5 的消息作为回调函数的参数调用。

注:这个流程图中的一个疑问点是为什么需要通信两次,第一次 修改 iframe 的 src 值,通知 NA 有消息,NA 再去调用 JsBridge 里面的方法,JsBridge 的方法再修改 iframe 的 src 值,src 值变动再次被 NA 捕获,NA 再解析相应的数据,关于这个问题可以查看 github 上的 issue;

NA 调用 H5 详细流程


我们以音频下载举例:下载功能是 NA 实现,下载完成后 H5 页面内会显示相应的下载状态:已下载、未下载;触发的 H5 和 NA 交互流程如下:

  1. H5 需要在 App 启动时注册:监听下载状态,调用 JsBridge 的 registerHandler 方法,参数为通信名(listenDownload)和对应要执行的方法(即 H5 监听到 NA 传来的消息时,会触发的方法)


2. registerHandler 执行会存储 listenDownload 对应的方法;


3. 当 NA 页面用户点击了下载时,NA 会调用 _handleMessageFromNative 方法,并将下载的音频相关信息和最初与 H5 约定的点击下载时的名字listenDownload作为参数传递给_handleMessageFromNative


4. _handleMessageFromNative会根据映射获取listenDownload对应的方法名,并执行;H5 方法内会根据判断下载完成或者下载失败,并做相应的状态显示;

后续

在安卓的个别机型上使用 JsBridge 通信时,H5 连续两次跟 NA 通信,会存在消息被吞的情况,即 NA 的 shouldOverrideUrlLoading未被触发,我们当时采用延时的办法解决;该issue中也有提到此问题;

在混合式开发中,JsBridge 的安全性和兼容性比较好,很多开发仍然采用这种模式,理解其中的工作原理,可以帮助更快的定位问题;

详细的可自行阅读相关源码:https://github.com/lzyzsd/JsBridge

Android View 体系竟然还能这么理解?

很多小伙伴可能在学习view的绘制流程源码的时候有点抓不住重点,所以在分析代码的时候绕来绕去脑袋晕乎乎的。今天我就来给大家化繁为简,只关注它最核心的东西。

发布于:4天以前  |  50次阅读  |  详细内容 »

移动端常见崩溃指标

崩溃分析,是将 Android 和 iOS 平台常见的 APP 崩溃问题进行归类分析,帮助企业根据崩溃指标快速发现、定位问题。

发布于:5天以前  |  78次阅读  |  详细内容 »

Android死锁初探

说到死锁,大家可能都不陌生,每次遇到死锁,总会让计算机产生比较严重的后果,比如资源耗尽,界面无响应等。

发布于:10天以前  |  119次阅读  |  详细内容 »

Android AES加密(Kotlin)

halo~最近工作上写的东西比较简单,感觉分享不出来,最近刚好看到数据加密这一块,感觉挺不错的,也挺好用的,所以下面分享给大家!

发布于:15天以前  |  107次阅读  |  详细内容 »

Android 持续滑动布局 ConsecutiveScrollerLayout 的使用

在开发项目的时候,有时候会遇到一些比较复杂的页面,需要多个不同的列表或者滑动布局、甚至是WebView,组成一个完整的页面。要实现这样一个复杂的页面,在以前我们可能会通过布局嵌套的方式,在一个大的ScrollView下嵌套多个RecyclerView、WebView、ScrollView来实现。但是这种嵌套的方式不仅会严重影响布局的性能,而且处理滑动事件的冲突也是一件头疼的事,处理不好会严重影响用户操作的体验。

发布于:19天以前  |  129次阅读  |  详细内容 »

Android 升级适配爬坑历程

最近接手了一个公司项目,项目比较老了,从Android 5.0之后就再也没有适配过了,然而重写时间又来不及,然后我的爬坑之旅便开始了。(以下适配方案是按照项目需求顺序来的)

发布于:22天以前  |  210次阅读  |  详细内容 »

2020年,一文点破跨平台开发框架现状

多年来,跨平台移动开发已经获得了最流行软件开发趋势之一的声誉。这并不令人意外,因为采用跨平台开发技术使得软件工程师使用同一代码就能为不同平台构建应用程序,从而节省时间、金钱以及不必要的工作。

发布于:1月以前  |  225次阅读  |  详细内容 »

最多阅读

简化Android的UI开发 7月以前  |  195904次阅读
Android设计与开发工作流 7月以前  |  2319次阅读
Google Enjarify:可代替dex2jar的dex反编译 1年以前  |  2313次阅读
Android多渠道打包工具:apptools 1年以前  |  1936次阅读
Android权限 - 第一篇 1年以前  |  1907次阅读
Google Java编程风格规范(中文版) 1年以前  |  1890次阅读
Stetho 1年以前  |  1826次阅读
Android UI基本技术点 1年以前  |  1816次阅读
30分钟搭建一个android的私有Maven仓库 1年以前  |  1781次阅读
2015 Google IO带来的新 Android 开发工具 1年以前  |  1715次阅读
你应该知道的布局和属性 1年以前  |  1665次阅读
听FackBook工程师讲*Custom ViewGroups* 1年以前  |  1661次阅读
Gradle小知识#3:任务的顺序 1年以前  |  1640次阅读
MVP在Android平台上的应用 1年以前  |  1639次阅读