Hybrid-APP技术原理

01/25 11:03
阅读数 41

源宝导读:Hybrid-APP技术不仅具有“Native APP的良好交互体验”同时也具备“Web APP跨平台开发的优势”。既然Hybrid-APP有这么多优势,那么究竟什么样的APP才算Hybrid App呢?本文将分享我们的技术研究成果。

一、什么是Hybrid-APP

  • 狭义的Hybrid:

    • 也是现在大家普遍认知的,Hybrid就是一种给 WebView 增加一些js通信可以调用原生API的方式。

  • 广义Hybrid:

    • 前端的开发思路与客户端原生的开发思路相结合。

    • 通过原生的配合,把原本js or 前端开发做不到的事情做到了,用原生的方式增强了原本的前端技术能力。

    • WebView+Bridge、RN、weex、小程序。

    我能否认为,只要是前端的开发思路与客户端原生的开发思路相结合,就认为他是一种Hybrid?
    通过原生的配合,把原本js or 前端开发做不到的事情做到了,用原生的方式增强了原本的前端技术能力,是否就是一种 Hybrid?
    无论是WebView+Bridge也好,RN类似的原生渲染框架也好,小程序也好,某种意义上讲,他们都算Hybrid?

Hybrid框架-基本能力

    接下来我们来看一下,一个Hybrid框架所需要具备的基本能力:

  • 跨平台能力。

    这也是Hybrid应用与原生应用相比最大的优点,一次编写随处运行。

  • 灵活的业务模块扩展能力。

  • 良好的调用原生功能的能力。

    由于在APP中有些功能必须由原生端提供,所以还需要有良好的调用原生功能的能力。

  • 快速更新迭代的能力。

    使用原生技术开发的APP每次更新都需要上传应用商店审核,但是使用Hybrid技术开发的应用可以绕过应用商店实现热更新。

二、Hybrid-APP中通信方案

    在Hybrid APP中最核心的技术就在于前端与客户端如何通信,接下来我们看一下,js与native之间是如何通信的。

2.1、JS 调用 Native 的几种方法

  • 假跳转的请求拦截

    1. A标签跳转。

    2. 原地跳转。

    3. iframe跳转。

  • 弹窗拦截

    1. alert()

    2. prompt()

    3. confirm()

  • JS上下文注入

    1. 苹果JavaScriptCore注入。

    2. 安卓addJavascriptInterface注入。

    3. 苹果scriptMessageHandler注入。

2.2、Native 调用 JS 的几种方法

  • evaluatingJavaScript 直接注入执行JS代码

    JS是一个脚本语言,任何一个JS引擎都可以在任意时机直接执行JS代码,我们可以把任何Native想要传递的消息/数据直接写进JS代码中。

  • loadUrl 浏览器用’javascript:’+JS代码做跳转地址

    在浏览器中,可以直接用’javascript:xxxx’来简单的执行一些JS代码,这个方法只有安卓可以用,因为iOS必须先将url字符串生成Request再交给webview去load

  • WKUserScript WKWebView的 addUserScript 方法

    WKWebView官方提供了一个Api,可以让WebView在加载页面的时候,自动执行注入一些预先准备好的JS

2.2.1、假跳转的请求拦截

    假跳转的请求拦截 就是由网页发出一条新的跳转请求,跳转的目的地是一个非法的压根就不存在的地址。

    url地址由协议、域名、路径、参数这么几个部分构成,我们可以构建一条假的url:用协议与域名当做通信识别、用路径当做指令识别、用参数当做数据传递。

    客户端会无差别拦截所有请求,真正的url地址应该照常放过,只有协议域名匹配的url地址才应该被客户端拦截,拦截下来的url不会导致webview继续跳转错误地址,因此无感知,相反拦截下来的url我们可以读取其中路径当做指令,读取其中参数当做数据,从而根据约定调用对应的native原生代码。

  • JS发起调用

    JS其实有很多种方式发起假请求,跟发起一个新请求没啥两样,只要按协议约定 生成假请求地址,正常的发起跳转即可,任何一种方式都可以让客户端拦截住。

  • 客户端拦截

    1. 安卓的拦截方式:shouldOverride UrlLoading。

    2. UIWebView的拦截方式:webView: shouldStartLoadWithRequest :navigationType。

    3. WKWebView的拦截方式:webView: decidePolicyForNavigationAction :decisionHandler。

2.2.2、弹窗拦截

  • JS发起调用

    可以使用alert/confirm/prompt三种弹框,每种弹框都可以由JS发出一串字符串,用于展示在弹框之上,而此字符串恰巧就是可以用来传递数据,我们把所有要传递通讯的信息,都封装进入一个js对象,然后生成字典,最后序列化成json转成字符串。

  • 客户端拦截

    1. 安卓的拦截方式:onJsPrompt。

    2. UIWebView的拦截方式:不支持截获任何一种弹框。

    3. WKWebView的拦截方式:webView: runJavaScriptText InputPanelWith Prompt :balbala。

2.2.3、苹果UIWebview JavaScriptCore注入

  • 客户端注入

    UIWebView可以通过KVC的方法,直接拿到整个WebView当前所拥有的JS上下文documentView.webView.mainFrame.javaScriptContext。
    拿到了JSContext,一切的使用方式就和直接操作JavaScriptCore没啥区别了,我们可以把任何遵循JSExport协议的对象直接注入JS,让JS能够直接控制和操作。

  • JS调用

  在没经过客户端注入的时候,直接使用调用callNativeFunction()会报 callNativeFunction is not defined这个错误,说明此时JS上下全文全局,是没有这个函数的,调用无效。
  在执行完客户端注入后,此时JS上下文全局对象下面,就拥有了这个callNativeFunction的函数对象,就可以正常调用,从而传递数据到Native。

2.2.4、安卓addJavascriptInterface注入

  • 客户端注入

    安卓的WebView有一个接口addJavascriptInterface,可以在loadUrl之前提前准备一个对象,通过这个接口注入给JS上下文,从而让JS能够操作,这个操作方式很类似苹果UIWebview JavaScriptCore注入,整个机制也差别不大。

  • JS调用

    在android端注入的对象同样也被挂载在JS上下文全局对象下面,直接访问即可调用。

2.2.5、苹果WKWebView scriptMessage Handler注入

  • 客户端注入

    苹果在开放WKWebView这个性能全方位碾压UIWebView的web组件后,也大幅更改了JS与Native交互的方式,提供了专有的交互APIscriptMessageHandler
需要注意的是如果当前WebView没用了,需要销毁,需要先移除这个对象注入,否则会造成内存泄漏,WebView和所在VC循环引用,无法销毁。

  • JS调用

   这里不像前边两个注入一样,直接注入到JS上下文全局对象里,addScriptMessageHandler方法注入的对象被放到了,全局对象下一个Webkit对象下面。
    并且调用方式和之前的两种方法也不同,前两种都可以让js任意操作所注入自定义对象的所有方法,而addScriptMessageHandler注入其实只给注入对象起了一个名字nativeObject,但这个对象的能力是不能任意指定的,只有一个函数postMessage。

2.2.6、evaluatingJavaScript 执行JS代码

    前面也简单介绍了一下,JS是一个脚本语言,可以在无需编译的情况下,直接输入字符串JS代码,直接运行执行看结果,这也是为什么在Chrome里,在网页运行的时候打开控制台,可以输入各种JS指令的看结果的。

    也就是说当Native想要调用JS的时候,可以由Native把需要数据与调用的JS函数,通过字符串拼接成JS代码,交给WebView进行执行。

    Android/iOS-UIWebView /iOS-WKWebView,都支持这种方法,这是目前最广泛运用的方法。

2.2.7、loadUrl 执行JS代码

    安卓在4.4以前是不能用evaluatingJavaScript 这个方法的,因此之前安卓都用的是webview直接loadUrl,但是传入的url并不是一个链接,而是以”javascript:”开头的js代码,从而达到让webview执行js代码的作用。

2.2.8、WKUserScript 执行JS代码

    对于iOS的WKWebView,除了evaluatingJavaScript,还有WKUserScript这个方式可以执行JS代码,他们之间是有区别的,这个虽然是一种通信方式,但并不能随时随地进行通信。

  • evaluatingJavaScript 是在客户端调用的时候js端会立刻执行代码。

  • WKUserScript 是预先准备好JS代码,当WKWebView加载Dom的时候,执行当条JS代码。

2.3、JS主动调用Native的方案对比

三、cordova运行原理

    在了解了Hybrid app核心的通信方案之后,我们接下来看看目前公司使用最广泛的跨平台技术cordova的运行原理是怎么样的。

3.1、客户端入口

    这里客户端以Android端为例分析,Android端默认的入口是mainActivity类,我们可以看到它其实继承CordovaActivity类,一切初始化条件是从loadUrl方法开始。

3.2、Android端核心类CordovaActivity

    CordovaActivity内依赖一个WebView类,一个Preferences类,一个CordovaInterface接口,并同时初始化一些配置信息。WebView具体实现是由CordovaWebViewImpl类,CordovaInterface接口具体实现是由CordovaInterfaceImpl类实现。

    CordovaWebViewImpl是核心类,里面会把一些插件能力初始化,用一个PluginManager进行管理,包含一个引擎类—CordovaWebViewEngine,这个引擎是通过反射的方式创建,自身初始化的时候把NativeToJsMessageQueue关联起来,里面包含着以Js字符串为主的双向链表,把每次从前端通过JS代码存储起来,然后通过绑定的桥接方式Pop出到相应的Native代码中去。

    最终实现由SystemWebViewEngine类来对Android系统中WebView控件进行二次包装,这个类的初始化是在CordovaWebViewImpl类反射创建,相关插件和消息传递也是通过SystemWebViewEngine进行绑定。

3.3、JS端cordova初始化

    Android端调用loadUrl后会启动webview加载前端代码,首先会加载运行cordova.js中的代码,在cordova.js中会运行cordova/init模块对cordova进行一个初始化,初始化中主要的核心操作就是:检查监听核心事件是否触发、平台初始化工作、加载插件js。

3.4、JS端平台启动处理

    不同的平台中平台启动处理的模块会有一些差异,但是核心处理相差不大,在Android平台中主要进行了三个处理:初始化通信模块、处理物理按键的事件、在onCordovaReady事件被触发时通知原生端展示webview。

3.5、JS端插件加载处理

    插件js加载处理中主要先会通过load方法加载cordova_plugins.js,获取到项目中用到的插件,然后通过injectScript方法加载插件js,可以看到整个加载过程都是通过添加script标签进行加载的,所以一旦插件数量很多对加载速度会有一定的影响。

    这里只是加载插件的js代码,原生端的插件加载并不是在这里进行的。

3.6、Cordova启动流程

    最后来总结一下整个cordova的启动流程,主要做了三个大的事情:

  • 原生端启动webview加载前端代码。

  • 初始化插件。

  • 建立通信通道。

3.7、Cordova通信流程

    启动cordova后,在项目运行的过程中当前端需要调用native的能力时,就需要与native端进行通信,Cordova通信流程中主要有这么几点:

  • Cordova通过在原生端与js端维护两个消息队列来处理消息回调。

  • Cordova在执行完exec()后,android会马上从消息队列中取出数据同步返回,但不一定就是该次请求的数据。

  • js端通过轮询获取原生端消息队列中的数据。

四、总结

    Hybrid 的宗旨就是,如果 WebView 本身做不到,或者做起来有很大限制或者性能不佳,那么可以让原生配合,一起做到。

    因为Hybrid本是一个面向业务服务的东西,如果业务的野心足够大,WebView 容器的想象空间应该是在能力上与RN/小程序看齐的,WebView 在 Hybrid 的支持下,不单纯是使用Bridge 调用几个原生 API 的事。

    我们完全可以拆解RN中的每个环节,把RN号称比 WebView 好的原生渲染/原生组件拆解融入 WebView,我也可以学习小程序保持 WXML/WXSS的开发方式,而非RN那样统一用JSX开发。

    这种拆解不是将所有框架优点塞在一个大而全的框架中,各种优化方案的选择背后一定带来的是一些取舍。谁来决定取舍,业务决定,如果自己能深度把握这里面的设计思想,就不用在乎什么新的轮子新的框架,取其设计优点,融入自己的业务之中即可。

------ END ------

作者简介

李同学: 前端研发工程师,目前负责天际移动平台的相关研发工作。

也许您还想看

微前端架构在容器平台的应用

前端数据层落地实践

移动建模平台元数据存储架构演进

AI云店小程序演变之路

基于 Go 的微服务运行情况监控实践

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部