Hybrid App的架构与实现

来源:互联网 发布:苹果手机打不开淘宝网 编辑:程序博客网 时间:2024/05/16 06:22

伴随着移动互联网产业的兴起,各式App层出不穷,我们迫切需求一种更快速、成本更低、技术更为成熟的项目开发方案,而H5的成熟让我们看到了希望,使得Web App开始崛起。为了提高用户体验,目前网站一般分为3个版本,简版、移动版、电脑版,简板Web普遍是基于WAP2.0开发的,界面视觉效果差、功能简单;移动版则普遍使用H5开发,适用于现在大家普遍使用的移动上网设备,拥有更好的视觉效果、交互方式,迥然不同于简板Web的设计风格;而电脑版在此则不做讨论。个人认为现在的Web App就是由移动版网页发展而来。


目前App大致分为三个大类

  • Web App

    定义:将所有功能都放在Web上展现,运行基于本地浏览器。在此将给Web简单的套一层App外壳的应用也归入Web App。完全采用HTML/CSS/JS编写,专为触摸操作进行了优化。目前iOS已禁止简单的套壳App上架。优点:开发速度快,跨平台,成本低,实时迭代用户无需更新缺点:网络速度要求高、服务器压力大,系统级别API调用难度大,用户体验差、用户留存度低

  • Native App

    定义::NativeApp是基于手机本地操作系统并使用原生语言编写的 。因为位于平台层上方,向下访问和兼容的能力会比较好一些,可以 支持在线或离线访问,消息推送或本地资源访问,摄像拨号功能的调 取。但是由于设备碎片化,App的开发成本要高很多,维持多个版本 的更新升级比较麻烦,用户的安装门槛也比较高。

    优点:用户体验佳、交互风格与系统吻合,节省流量,可访问本地资源,速度快,用户留存度高缺点:成本高,版本迭代慢,需要过审

  • Hybrid App

    定义:介于Web App与Native App的一种折中方案,底层(框架)部分由iOS/Android开发人员处理,上层(内容展现)部分由Web前端人员处理,用户界面操作逻辑及部分静态资源驻留本地,使得Web App可以对操作迅速反应并在很大程度上实现离线访问。Hybrid App追求趋近于原生App的体验,但目前还较困难。

三者之间的比较

需要考虑的方面

  • 分清Native与前端的界限,Native提供宿主环境,前端需要合理的利用Native提供的资源,提升用户体验及自身性能。在设计上需要考虑以下问题:
  • 交互设计:如何设计与前端的交互?Native需要考虑提供NativeUI/Header/消息/Alert等组件接口、通讯录/系统/设备信息读取接口、Native/H5相互跳转(H5跳Native、H5新开WebView跳转、Native跳转H5)等问题。
  • 数据访问:Native如何访问H5资源(File方式访问H5本地静态资源/URL方式访问服务器资源)。资源增量替换是Android的,iOS不用考虑。
  • 登录(及支付/分享等)模块:这些模块具体由谁实现?Native/Web一方操作怎样使另一方收到对应信息并处理?(支付/分享建议Native端来做,都有相应的SDK)
  • 开发调试:Native与前端需要商量出一套可开发调试的模型,不然很多业务开发的工作将难以继续。

Hybrid交互设计

UIWebView由于其API难用、还有内存泄漏,现在已经弃用,iOS8以上都应该尽量用新的WKWebview。WK提供了一系列API来使得Native与Web的信息交换简单高效。还有一个不可忽视的一点是WK使用与Safari相同的JS引擎+内置手势+无内存泄漏,是UIWebView的替代者。所以在这里UIWebView只做简要介绍。

  • UIWebView Native调JS方法 webView.stringByEvaluatingJavaScriptFromString()

    JS调Native方法 UIWebView没有办法直接使用js调用app,但是可以通过拦截request的方式间接实现JS调用Native方法,另一种是使用JavaScriptCore的jsContext注册objc对象或使用JSExport协议导出Native对象的方式。本文主要介绍第一种实现,第二种实现方式不再赘述。

     func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool{          //如果请求协议是hello 这里的hello来自js的调用,在js中设为 document.location = "hello://你好";          //scheme:hello ,msg:你好          //通过url拦截的方式,作为对ios原生方法的呼叫          if request.URL?.scheme == "hello"{              let method:String = request.URL?.scheme as String!              let sel =  Selector(method+":")              self.performSelector(sel, withObject:request.URL?.host)              request.URL?.path              //如果return true ,页面加载request,我们只是当做协议使用所以不能页面跳转              return false          }          return true      }

函数回调/返回值
Native调JS可以有返回值,但是JS调Native是通过间接的拦截request方式实现,它根本就不算方法调用,所以应该是不存在可以直接产生返回值的。当然如果需要Native对JS的调用有所响应,可以通过回调函数的方式回应JS。可以在调用Native的时候增加一个JS回叫函数名 app在处理完之后调用回调函数并把需要的参数通过回调函数的方式进行传递。

  • WKWebView
    Web脚本注入
    WKUserScript 允许在正文加载之前或之后注入到页面中。这个强大的功能允许在页面中以安全且唯一的方式操作网页内容。
    WKUserScript 对象可以以 JavaScript 源码形式初始化,初始化时还可以传入是在加载之前还是结束时注入,以及脚本影响的是这个布局还是仅主要布局。于是用户脚本被加入到 WKUserContentController 中,并且以 WKWebViewConfiguration 属性传入到 WKWebView 的初始化过程中。这个样例可以简单扩展为更为高级的页面修改方法,例如去除广告、隐藏评论等。

     let source = "document.body.style.background = \"#777\";" let userScript = WKUserScript(source: source, injectionTime: .AtDocumentEnd, forMainFrameOnly: true)  let userContentController = WKUserContentController()  userContentController.addUserScript(userScript)  let configuration = WKWebViewConfiguration()  configuration.userContentController = userContentController  self.webView = WKWebView(frame: self.view.bounds, configuration: configuration)

    Native调JS方法

      //直接调用JS   webView.evaluateJavaScript("hi()", completionHandler: nil)      //调用JS带参数      webView.evaluateJavaScript("hello('liuyanwei')", completionHandler: nil)      //调用JS获取返回值      webView.evaluateJavaScript("getName()") { (any,error) -> Void in          NSLog("%@", any as! String)      }

    WK通过与UIWebView类似的方法调用JS语句,但获取返回值的方式不同,WKWebView用的是回调函数获取返回值。

    JS调Native方法
    Web中的信息也可以通过调用这个函数被传给Native里:

     var message = {                          'method' : 'hello',                          'param1' : 'haibao',                          }; window.webkit.messageHandlers.webViewApp.postMessage(message); //这个 API 真正神奇的地方在于 JavaScript 对象可以自动转换为 Objective-C 或 Swift 对象。 //Native中Handler的注册handler需要在WKWebView初始化之前 config = WKWebViewConfiguration()           //注册js方法          config.userContentController.addScriptMessageHandler(self, name: "webViewApp")          webView = WKWebView(frame: self.webWrap.frame, configuration: config) //处理handler委托。ViewController实现WKScriptMessageHandler委托的func userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage)方法 //实现WKScriptMessageHandler委托       class ViewController:WKScriptMessageHandler       //实现js调用ios的handle委托       func userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage) {           //接受传过来的消息从而决定app调用的方法           let dict = message.body as! Dictionary<String,String>           let method:String = dict["method"]!           let param1:String = dict["param1"]!           if method=="hello"{               hello(param1)           }       }

    Alert拦截
    在WKWebview中,JS的Alert是不会出现任何内容的,你必须重写WKUIDelegate委托的runJavaScriptAlertPanelWithMessage message方法,自己处理Alert。类似的还有Confirm和Prompt也和Alert类似。

     Alert拦截方法 func webView(webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: () -> Void) {          completionHandler()          let alert = UIAlertController(title: "ios-alert", message: "\(message)", preferredStyle: .Alert)          alert.addAction(UIAlertAction(title: "ok", style: .Default, handler:nil))          alert.addAction(UIAlertAction(title: "cancel", style: .Cancel, handler: nil))          self.presentViewController(alert, animated: true, completion: nil)      }

    Native/Web端做好交互格式约定,Native端提供规范性的API供Web端访问。
    常用API:

    • 界面跳转(N->W/W->N/W->W/W->W新开WebView)+过场动画
    • Header组件
    • NativeUI相关组件(Loading遮罩等)
    • 分享
    • 推送
    • 登录

框架架构

架构选择MVC,因为上层逻辑都是Web端实现的,对于Controller的负担不是太重。
框架结构为TabBar+Navi。
Web部分将入口界面的样式资源存放本地,后续跳转及动态数据通过网络获取。
下图是参考的Web目录结构图。


Hybrid目录结构图

数据请求

约定调用请求格式

let data = {url:'requestURL',             param: {参数},             type: 'post'}

约定返回数据格式

let data = ["data": data,     //网络请求结果、本地数据等回传信息           "errno": errno,   //错误码            "msg": msg,       //描述            "callback": callback]    //回调ID

Web端通过传递一个字典将需要的请求信息交给Native处理,Native处理完毕后数据通过Web端回调。

let dataString = self.toJSONString(data)webView.stringByEvaluatingJavaScriptFromString(self.getRequest + "(\(dataString));")

本地数据访问

约定数据访问格式

let data = {name:'name',            type:'dataType'}

约定返回数据格式

let data = ["data": data,     //本地数据请求结果           "errno": errno,     //错误码            "msg": msg,       //描述            "callback": callback]    //回调ID

参考资料:

聊聊Web App、Hybrid App与Native App的设计差异
Hybrid App开发实战
iOS 8 WebKit框架概览
浅谈Hybrid技术的设计与实现1
浅谈Hybrid技术的设计与实现2
去啊App实战:极致的Hybrid混合式开发
三层架构
UIWebView和WKWebView的使用及js交互
WKWeb View