Hybrid设计与实现

11次阅读

共计 6486 个字符,预计需要花费 17 分钟才能阅读完成。

背景及概述

随着移动浪潮的兴起,各种 APP 层出不穷,极速的业务扩展提升了团队对开发效率的要求,这个时候使用 IOS&Andriod 开发一个 APP 似乎成本有点过高了,而 H5 的低成本、高效率、跨平台等特性马上被利用起来形成了一种新的开发模式:Hybrid APP。
作为一种混合开发的模式,Hybrid APP 底层依赖于 Native 提供的容器(UIWebview),上层使用 Html&Css&JS 做业务开发,底层透明化、上层多多样化,这种场景非常有利于前端介入,非常适合业务快速迭代,于是 Hybrid 火啦。
Hybrid App 主要以 JS+Native 两者相互调用为主,从开发层面实现“一次开发,多处运行”的机制,成为真正适合跨平台的开发。Hybrid App 兼具了 Native App 良好用户体验的优势,也兼具了 Web App 使用 HTML5 跨平台开发低成本的优势。

目前已经有众多 Hybrid App 开发成功应用,比如美团、爱奇艺、微信等知名移动应用,都是采用 Hybrid App 开发模式。

为什么要引入 hybrid 开发

Native 应对急速业务需求,APP 迭代加快,频繁的发版已难以应付
Android 渠道众多,apple store 审核周期长
纯 native 开发效率低,开发维护成本高
不能及时修复 bug, 不能动态发版
相对于其他动态发布技术,技术研发成本较低,使用语言更广泛,社区资源更丰富

Hybrid 开发优劣势

三种开发模式对比

Native App 即 原生 App 开发

<font color=#A52A2A size=3 face=” 黑体 ”> 优点 </font>

(1)打造完美的用户体验
(2)性能稳定
(3)操作速度快,上手流畅
(4)访问本地资源(通讯录,相册)
(5)设计出色的动效,转场,
(6)拥有系统级别的贴心通知或提醒
(7)用户留存率高

<font color=#A52A2A size=3 face=“黑体”> 缺点 </font>

(1)分发成本高(不同平台有不同的开发语言和界面适配)
(2)维护成本高(例如一款 App 已更新至 V5 版本,但仍有用户在使用 V2,V3,V4 版本,需要更多的开发人员维护之前的版本)
(3)更新缓慢,根据不同平台,提交–审核–上线 等等不同的流程,需要经过的流程较复杂。

Web App 即 网页 App 开发

  • 特点(html css js)
  • 优势 (发版完全自控随时更新开发成本小时间快)
  • 劣势(性能差弱网络无网络条件下体验差)Web App 其实就是写好的一套长得像 App UI 界面的能够自适应的网页加壳。本质套 webview 壳子打包成 App,走的都是 web 页面(html css js),这种方式对于做过 Web 开发的开说非常轻松就可以做出一个属于自己的 App,因为本身来说用的就是 Web 的东西,所以有非常好的跨平台的特性可以在任意平台运行,包括发版这方面 web 可以随时部署所以不需要发版,web 页面嵌入 webview 开发起来速度非常快,一个人就可以轻松搞定,对有展示类需求的项目来说采用这种方式是最适合的,但是如果要实现的功能比较复杂的话就显得力不从心了。
    相比 Native App,Web App 体验中受限于网络环境和渲染性能。
  1. 网络环境,渲染性能
    Web APP 对网络环境的依赖性较大,因为 Web APP 中的 H5 页面,当用户使用时,去服务器请求显示页面。如果此时用户恰巧遇到网速慢,网络不稳定等其他环境时,用户请求页面的效率大打折扣,在用户使 用中会出现不流畅,断断续续的不良感受。同时,H5 技术自身渲染性能较弱:对复杂的图形样式,多样的动效,自定义字体等的支持性不强。

因此,基于网络环境和渲染性能的影响,在设计 H5 页面时,应注意以下几点:

1. 简化不重要的动画 / 动效
2. 简化复杂的图形文字样式
3. 减少页面渲染的频率和次数

Hybrid App 即 混合型 App 开发

<font color=#A52A2A size=3 face=” 黑体 ”> 优点 </font>

1、跨平台
2、开发周期短、成本低
3、用户体验良好
4、可以即时修复 bug、动态发版

<font color=#A52A2A size=3 face=“黑体”> 缺点 </font>
1、虽然说你可以专注在界面以及交互开发上了,但是这页会成为一个缺点,比如说要仿造一个 iOS 的默认设置界面,就需要大量的 html 以及 css 代码了,而且效果不一定和 iPhone 上面的界面一样好;
2、正因为这是跨平台的开发,所以还是这句话:兼容是前端的痛。了解过在 Android 机器上面的 Web 开发就知道这个痛了。比如前些年在 Android 设备上面写圆角,border-radius:10px,在 Android 的设备上面会出现毛边。
3、便于调试其实是在 Web 界面层的。但是实际上做 Hybrid App 开发的时候,你会遇到需求,进入手机的底层请求,做某些处理。比如说如果该应用有 Push Notification 服务的话,你就需要到底层,获取 Push Notification 发生时的数据,以及做相应的交互处理。当然类似 PhoneGap 这类框架,已经有很好的插件机制去帮助你解决类似的问题,当然还有 Game Center 之类的插件,具体的话可以到 Github 去关注 PhoneGap 官方的账户,资源非常丰富

前端与 native 分工

在做 Hybrid 架构设计之前需要分清 Native 与前端的界限,首先 Native 提供的是一宿主环境,要合理的利用 Native 提供的能力,要实现通用的 Hybrid 平台架构,站在前端视角,我认为需要考虑以下核心设计问题。

交互设计

Hybrid 架构设计第一个要考虑的问题是如何设计与前端的交互,如果这块设计的不好会对后续开发、前端框架维护造成深远的影响,并且这种影响往往是不可逆的,所以这里需要前端与 Native 好好配合,提供通用的接口,比如:

① NativeUI 组件,header 组件、消息类组件

② 通讯录、系统、设备信息读取接口

③ H5 与 Native 的互相跳转,比如 H5 如何跳到一个 Native 页面,H5 如何新开 Webview 做动画跳到另一个 H5 页面

资源访问机制

Native 首先需要考虑如何访问 H5 资源,做到既能以 file 的方式访问 Native 内部资源,又能使用 url 的方式访问线上资源;需要提供前端资源增量替换机制,以摆脱 APP 迭代发版问题,避免用户升级 APP。这里就会涉及到静态资源在 APP 中的存放策略,更新策略的设计,复杂的话还会涉及到服务器端的支持。

账号信息设计

账号系统是重要并且无法避免的,Native 需要设计良好安全的身份验证机制,保证这块对业务开发者足够透明,打通账户信息。

Hybrid 开发调试

功能设计完并不是结束,Native 与前端需要商量出一套可开发调试的模型,不然很多业务开发的工作将难以继续,这个很多文章已经接受过了,本文不赘述。

至于 Native 还会关注的一些通讯设计、并发设计、异常处理、日志监控以及安全模块因为不是我涉及的领域便不予关注了(事实上是想关注不得其门),而前端要做的事情就是封装 Native 提供的各种能力,整体架构是这样的

Hybrid 交互设计

Hybrid 交互有两种:
1、native 主动调用前端 js 方法主动与前端通信
2、H5 主动与 native 通信是通过 url schema 方式进行的,首先要和 native 约定好通信的 schema 如:wubacst 然后根据 Android 和 iOS 不同的通信方式来分别约定 其中 Android 是在通信之前先创建一个 iframe 通过 iframe 把拼接好的 url 请求发送到 native 或者通过拦截 prompt 中的消息来和前端通信 iOS 使用了 iOS8 以上的 wkwebview 中的 messageHandlers 的 postMessage 方法直接把 url 请求发送到 native native 接收到前端发送的请求解析之后通过回调函数来通知前端

两者通信的桥梁都是 webview

Hybrid 交互原理

4.1.1、WKWebView+WKScriptMessageHandler 实现 JS 与 OC 之间互相通信

iOS 基于 WebKit 框架的中的 WKWebView+WKScriptMessageHandler 实现 JS 与 OC 之间互相通信。
整体交互过程分为三步:
第一:通过 - (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name,搭建 JS 调用 OC 的桥梁,注册供 JS 调用的方法 name;
第二:JS 在调用 OC 注册方法只需要:window.webkit.messageHandlers.<name>.postMessage(<messageBody>)
第三:客户端在 WKScriptMessageHandler 代理方法里收到 JS 的调用,通过解析处理,并按照事先与 FE 约定好的的协议做方法的映射,从而达到 JS 事件的响应。

4.1.2、UIWebview 的 javaScriptcore 方式

将 UIWebViewDelegate 与 UIWebView 拆分成了 14 类与 3 个协议, 包含该更细节功能的实现

  • (void)evaluateJavaScript:(NSString )javaScriptString completionHandler:(void (^ _Nullable)(_Nullable id, NSError _Nullable error))completionHandler

WKWebView:

1、在性能、稳定性、功能方面有很大提升
2、更多的支持 HTML5 的特性
3、官方宣称的高达 60fps 的滚动刷新率以及内置手势
4、Safari 相同的 JavaScript 引擎

Hybrid 通信机制

4.2.1、整体设计

4.2.2、native 与 js 通信流程

4.2.3、js 与 native 交互流程

类似于 JOSNP 跨域原理
APP 在安装之后会注册私有协议到 OS,浏览器自身不能识别的协议时,会将链接抛给 OS,转为 APP 处理
比如 itunes:// 开头的链接是 Apple Store 的私有协议,支付宝的私有协议 alipay://,腾讯的 tencent:// 等等
固定协议:webview.loadUrl(“javascript: alert(‘hello world’)”);

交互协议设计

API 式交互

调用 native API 接口方式和我们请求服务端的 ajax 方式类似,都是前端主动去发送一个请求,被接收处理后返回回调

交互数据格式约定

混合开发中交互种类繁多,如何做到通用
以 58 同城车商通 app 为例:
页面跳转类型

wubacst://jumppage/…?query=…

native 组件处理类型

wubacst://handlejs/…?query=…

前端协议封装

前面提到过,Android 和 iOS 对于前端的交互是有区别的,虽然都是采用 URL 拦截的方式,但是具体的方式是不一样的;iOS 采用 WKwebview 中的 messageHandlers 方法,而 Android 采用类似 ajax 一样的请求;但是对于业务放来说,在和 native 通信时是不区分端的,这就需要封装一个交互协议的 API 来提供给业务方,在协议 API 中来区分处理 Android 和 iOS 的交互;

处理源码如下:

// 区分处理 Android 和 iOS
  _nativeBridge: function(param) {"android" == this.os ? this._andr4Native(param) : 
        "ios" == this.os ? this._ios4Native(param) : "ipad" == this.os && this._ios4Native(param)
    },

Android 客户端处理

_createIframe: function() {
        var iFrame;
        return iFrame = document.createElement("iframe"),
            iFrame.setAttribute("style", "display:none;"),
            iFrame.setAttribute("height", "0px"),
            iFrame.setAttribute("width", "0px"),
            iFrame.setAttribute("frameborder", "0"),
            iFrame
    }
 // 开始是使用 iframe 和 Android 客户端通信 后来改成了 prompt   
 _andr4Native: function(param) {
        var host = param.host;
        var path = param.path;
        var query = param.query;
        query = JSON.stringify(query)
        //3.5.0 以上版本 Android 端都走新的拦截方式
        var url = "wubacst://" + host + "/" + path + "?query=" + encodeURIComponent(query);
        if(this._versionCompare(this._getVersion(),'3.5.0')){window.prompt(url)
        }else{if (!this.domReady)
            return void this.actionPreQueue.push(param);
        this.andrFrame = this._createIframe(),
            this.andrFrame.src = "wubacst://" + host + "/" + path + "?query=" + encodeURIComponent(query),
            document.body.appendChild(this.andrFrame),
            this.andrFrame = null
        }
        
    }

iOS 客户端处理

//messageHandlers 上绑定的 WBcheshangtong 对象是和 iOS 约定好的
_ios4Native: function(param) {
        var host = param.host;
        var path = param.path;
        var query = param.query;
        query = JSON.stringify(query);
        var url = "wubacst://" + host + "/" + path + "?query=" + encodeURIComponent(query);
        if (window.webkit) {
            try {window.webkit.messageHandlers.WBcheshangtong.postMessage(url);
            } 
        }
    }

处理好底层交互之后,在根据业务的不同,前端封装一些通用的协议,如:

整个通信协议 API 如下:

app58.prototype = {init: function() {},
 _nativeBridge: function(param){},
_ios4Native: function(param){},
_andr4Native: function(param){},
_createIframe: function(){},
loadPage: function(path, url, jumpParameter,title,titleColor,isDestoryBeforePage){}
...
}
var WBAPP = new app58(window.app_config || null)

问题总结

iOS cookie 丢失问题

目前来说 WKwebview 还不是那么稳定,经常会出现前端 cookie 丢失问题,一下是本人在开发过程中解决问题的一个过程:

最终是用 hookAjax 来实现的
hookAjax 的原理就是拦截所有的网络请求,代理 send 方法实现自己的目的

有兴趣的可以去看看 hookajax 这里就不再详细讲解了

Android 通信协议重复提交

由于 Android 使用的是 iframe 发送请求和 Android 客户端进行交互的,在前端使用 canvas 时,iframe 重复执行,导致前端重复提交协议,目前换成了 prompt 交互;在开发过程中有遇到这种问题的小伙伴,欢迎留言。

正文完
 0