【原创】无框架开发Hybrid App

近年来,Hybrid App对于绝大部分开发者来说已经不是什么新的东西。各类Hybrid App开发框架也是如雨后春笋般破土而出。最早进入广大开发者视野的应该是PhoneGap,也叫Cordova,随即而来的还有AppCan、Titanium等等。但是使用框架一方面不方便使用者了解Hybrid的实现原理,另一方面,框架往往会给项目带来一些附加的问题,比如降低性能、代码冗余等等。最近,我本着亦学亦用的态度,也开始加入Hybrid App开发的行列,通过本文跟大家分享一下在无框架环境下,作为Web前端,我如何与移动端的同事一起进行Hybrid App开发。

Hybrid App 如何工作

简单的说,Hybrid App就是由Native App提供容器(UIWebView / WKWebView),承载Web(H5)页面。Native与H5之间通过JS调Native接口或Native调JS方法进行通信,完成一系列交互。

Hybrid App 因何而生

在Hybrid App出现之前,原生的Native App以及独立的H5 Web App各领风骚。原生App以其优越的性能、流畅的体验、华丽的交互深受使用者青睐,而Web App则以其轻量、迭代周期短、展现形式多样而被广泛应用。当然,也各存诟病,原生App界面展示不够灵活、版本发布周期太长(尤其是iOS)等,而Web App则是性能差、体验差等。

正是由于以上种种,Hybrid App悄然而生,意在结合二者的优势,去除二者的诟病,找到一个最佳结合点平衡发展,既能提升Web App的体验,又能避免Native App的限制。不仅如此,但二者结合以后,还能做到通过网页调用本地应用(比如:操作相机、相册等),制作一些高度融合的产品或应用场景,将优势最大化。

试想一下,在一个App中既有Native App的流畅体验,又有Web App灵活丰富的展现形式,并且还能随时更新升级,不用担心App发版周期长的问题,那将是一种怎样的体验?

Hybrid App 怎么做

对于Native端(以Android为例)

前文中提到,Hybrid App需要Native端提供一个承载Web页面的容器。为了更好的配合,我也去了解了一下Android是如何提供容器的。我们先来看一个图:

首先,Android App开发的时候需要在界面中创建一个WebView控件,并给该控件设置相应的ID,图中【1】所指的语句便是通过ID找到界面中的WebView控件。

然后,我们需要给WebView控件添加一些功能,最起码的在H5页面中我们经常需要使用JS来工作,所以我们需要设置其启用Javascript功能,正如图中【2】处所示。

第三,由于开发移动端Web页面使用调试工具比较麻烦,我们有时候还是只能通过简单粗暴的alert函数来解决问题,因此开发环境启用alert支持还是有必要的,然而WebView只是一个载体,真正内容的渲染需要依赖WebChromeClient类来完成,因此我们还需要给WebView设置一个WebChromeClient实例,如图中【3】处所示。

第四,别忘了我们提供容器的目的,是需要与H5页面进行交互(调用JS方法/提供方法给JS调用),因此我们还需要给WebView提供一个实例,用于向H5暴露调用Native方法的接口,正如图中【4】处所示。这句话将在WebView的Window对象中产生一个DIY实例对象,该对象包含了DemoJavaScriptInterface类中的接口方法,H5页面可以通过该对象与Native通信,如:

最后,【5】处所示的就是WebView加载即将在该WebView中展示的H5页面。

除此之外,我们还能通过WebSettings给WebView设置很多其他的功能:

大家可以通过Android Studio逐一查看试用。

对于Web端

对于Web端我们不会涉及界面相关的知识,也不会讲如何使用某种Hybrid App框架的使用方法。我会描述一些使用原生JS实现与Native通信的方法。

接上文,WebView已经在Window对象下面创建了DIY对象,因此我们JS也就将就使用该对象作为命名空间了。先来考虑一个问题,对于一个Hybrid App项目可能会涉及到很多需要Native端完成的功能,但是如果Native将所有的业务方法都直接暴露到DIY对象下,势必会难以维护,而且会带来一些其他的麻烦。因此,我们约定统一的入口,并以参数的形式告知Native端H5现在需要做什么,而在DIY下只保留极少的入口方法,比如:

假设,Native端只保留了以上两个入口,startSdkWork用于处理Native本地业务逻辑,httpRequest用来处理网络请求。接下来,我根据实际的Web与Native的通信流程进行的Web端的JS封装,先来看一下基本结构:

简单说明,callSdk与sdkCallback可以当做是关联的,callSdk用于启动Native端的处理逻辑,当Native处理完成后通过调用sdkCallback方法回传处理结果,进而有JS继续完成后续逻辑。而callJs与sdkCallback类似,都是供Native调用,不同在于callJs由Native端主动触发。hasMethod方法用于Web端在调用Native方法前判断Native端是否在DIY对象中暴露了某方法,避免随意调用导致逻辑报错。

由于iOS与Android以及UIWebView和WKWebView与Web端通信存在差异,这里补充描述一下callSdk方法的内部实现细节,直接看代码:

主要两点:

  1. WKWebView中不能直接通过parsedMethod(param)的方式调用,只能window.webkit.messageHandlers[entry].postMessage(param)来调用
  2. Android中WebView对上下文有要求,不能直接parsedMethod(param)调用,需要parsedMethod.call(that, param)的方式调用

最后,用一张图描述一下callSdk与sdkCallback的关系:

执行过程就是,Web端调用Native端功能的时候将回调方法名称通过callback传入Native,当Native处理完成后调用sdkCallback将回调方法回传,Web端通过回调方法名称确定后续的处理逻辑。如此一来功能由Web端发起,也由Web端处理结束,实现功能闭环。

路漫漫其修远兮,算是对Hybrid跨出了第一步。