跨平台技术发展的三个阶段
第一阶段是混合开发的web容器时代
- 为了解决原生开发的高成本、低效率,出现了Hybrid混合开发
- 原生中嵌入依托于浏览器的WebView
- Web浏览器中可以实现的需求在WebView中基本都可以实现
- 但是Web最大的问题是,它的性能和体验与原生开发存在肉眼可感知的差异
- 因此并不适用于对性能和用户体验要求较高的场景
第二阶段是以RN和Weex为代表的泛web容器时代
- RN对Web标准进行了功能裁剪
- 用户体验更接近于原生了
- 由于进行了功能裁剪,所以RN对业务的支持能力还不到浏览器的5%
- 因此仅适用于中低复杂度的低交互类页面。面对稍微复杂一点儿的交互和动画需求,都需要通过调用原生代码去扩展才能实现
第三阶段是以Flutter为代表的自绘引擎时代
- Flutter是构建Google物联网操作系统Fuchsia的SDK
- 它使用Dart语言开发APP
- 一套代码可以同时运行在iOS和Android平台上
- Flutter采用自带的Native渲染引擎渲染视图,它是自己完成了组件渲染的闭环
- 而RN、Weex之类的框架,只是通过JavaScript虚拟机扩展调用系统组件,最后是由Android或者iOS系统来完成组件的渲染
下面来看一下几类型的混合开发APP:
Web APP框架
ionic
Ionic框架是基于Web技术应用HTML、CSS以及Java技术进行智能设备APP开发的框架,Ionic框架是用来开发混合模式的移动APP开发框架;
优势
- 全套的UI组件
Ionic框架很注重外观的体验,所以它提供了很多UI组件帮助开发者开发APP,比如:下拉刷新、标签等。界面美观,开发者能够很快的上手,开发的APP都很实用。 - 代码容易维护
Ionic框架是基于AngularJS,也就支持AngularJS的特点,遵循标准的代码,维护代码就很容易,能够完美融合AngularJS - 支持跨平台
可以在主流的Android操作系统和ios操作系统上运行,或者其他的操作系统也可以支持 - 很多强大的命令行
- 强大的社区、框架适用范围广
能够编译成各个平台的应用程序
劣势
- 内存占用高
- 不适合做游戏类型app
- web技术无法解决一切问题,对于比较耗性能的地方无法利用native的思维实现优势互补,如高体验的交互,动画等
Cordova
Cordova提供了一组设备相关的API;通过这组API,移动应用能够以JavaScript访问原生的设备功能,如摄像头、麦克风等;Cordova还提供了一组统一的JavaScript类库,以及为这些类库所用的设备相关的原生后台代码;Cordova支持如下移动操作系统:iOS, Android,ubuntu phone os, Blackberry, Windows Phone, Palm WebOS, Bada 和 Symbian。
通信
通信原理
- 保存Cordova_plugin.js的 插件文件名字和地址
- 插件的API呼出时,通过调用Cordova的exec模块将API的参数保存在CommandQueue的队列中。 CALLBACK则保存在JS侧的callbacks map里面
- 添加一个空的iframe,iframe的src则指向gap://ready
- 3的iframe的src设置以后,NATIVE侧UIWebviewDelegate#shouldStartLoadWithRequest则被呼出来
- Webview的Delegatet判断gap://ready的情况下,则执行commandDelegate的处理
- commandDelegate则从JS侧取出API的参数,内部实现则是通过 UIWebview#stringByEvaluatingJavaScriptFromString的返回值 取得CommandQueue里面的参数转换成JSON数据
- 根据6的插件,执行NATIVE定义的插件实例
- 插件中,有CALLBACK的情况下,成功失败的结果通过UIWebview#stringByEvaluatingJavaScriptFromString执行JS,JS端则根据传过来的CALLBACKID,从callbacks map取出回调函数并执行
通信方式
- iframe的方法(默认)
- xmlHttpRequest的方法(iOS5.x版本因为 -webkit-scroll的IFRAME有BUG,则推荐使用)
插件导入流程
Native
- APP启动,MainViewController初始化之时,queue和command的DELEGATE初期化 - config.xml文件解析,插件名设置到数组,插件文件和插件名设置到pluginMap,属性设置到setting - 在Webview类里面,加载index.html,index.html里面加载cordova.js、开始初期化
JS
- 加载cordova.js时、内部的事件设置模块,NATIVE交互模块,初期化模块,插件加载 - 插件模块是cordova_plugins.js文件定义的插件文件地址,文件名保存的MAP - deviceready事件发布后,插件的API可以使用了 - 插件API执行后,模块MAP将插件文件加载,执行exec函数 - 在index.html里面添加一个空的iframe、指定src=gap://ready,通知到Nativie
优势
- iOS和Android基本上可以共用代码;
- 纯web思维,开发速度快, 简单方便,一次编码,到处运行;
- 如果熟悉web开发,文档很全, 系统级支持封装较好,所有UI组件都是有html模拟,可以统一 使用;
- 可实现在线更新,允许动态加载web js;
- 文档多,开发者多,遇到问题容易解决,技术成熟;
劣势
- 占用内存高一些;
- 不适合做游戏类型app;
- web技术午无法解决一 切问题,对于比较耗能的地方无法利用native的思维实现优势互 补,如高体验的交互,动画等。
Hybrid APP(Webview)
利用 安卓和 iOS 上的 webview 容器,APP 能够执行 html、css 和 js 脚本,展示 web 页面。如果需要原生功能就添加 bridge 供 java 调用。具有开发效率高、跨平台、支持动态发布等特点,它是目前应用最广泛最成熟的一种方案;
Webview通信
假跳转的请求拦截(不建议)
- 假跳转的请求拦截 就是由网页发出一条新的跳转请求,跳转的目的地是一个非法的压根就不存在的地址
- 比如:wbcst://testhost/action?params=xxx
- 模拟http协议网络请求 scheme://host/action?params
- 客户端会无差别拦截所有请求,真正的url地址应该照常放过,只有协议域名匹配的url地址才应该被客户端拦截
JS调用方式
- a标签跳转
- location.href跳转
- iframe跳转
- 不建议使用,android系统对url参数做了字节限制,无法进行大数据的通信
弹窗拦截(不建议)
alert
- 弹出个提示框,只能点确认无回调
confirm
- 弹出个确认框(确认,取消),可以回调
prompt
- 弹出个输入框,让用户输入东西,可以回调
- 不建议使用,会无差别的拦截所有前端的window弹窗
JS上下文注入(推荐)
iOS
- WKWebView scriptMessageHandler注入
android
- addJavascriptInterface注入
特点
- 不通过任何拦截的办法,而是直接将一个native对象(or函数)注入到JS里面,可以由web的js代码直接调用,直接操作
WebView 渲染引擎设计的上的缺陷
- JS Execute,Layout, Paint 都在MainThread ,无法并行化。
- JS 的性能赶不上 Native Tookit 的 Java Dart Object-C 等编译型语言,执行复杂逻辑时会卡顿。
- 渲染流水线非常长,导致浏览器对合成器动画和非合成器动画区分对待,非合成器动画性能不佳。
- OpenGL 设计上是推荐单线程模型,一个 Context 同时只能运行一个线程使用。 GPU Thread 运行在单独 GPU 进程, Render 进程无法访问 GPU 进程的 OpenGL Context ,两个进程无法 Texture 共享资源。 Render 进程只能输出 Bitmap/Command Buffer 通过 IPC 传递给 GPU 进程,无法直接在 GPU 进程的 Open GL Context 做直接光栅化,难以充分发挥现代 GPU 的性能。
- 光栅化是异步进行的,进行惯性滚动时,会出现白屏,这个是 Webview 始终无法避免的问题。
- 设备平台众多,需要兼容CPU渲染,无法进行 All In GPU 的设计。
优势
- 跨平台
- 开发周期短、成本低
- 用户体验良好
- 可以即时修复bug、动态发版
劣势
- 仿原生iOS效果复杂
- 机型兼容性
ReactNative/Weex跨平台技术
这种技术最大化的复用前端的生态和 Native 的生态体系,把 Native View 的高性能组件积累输出给前端的技术体系。此方案和浏览器的最大区别在于 Script 的执行和 Native View 渲染体系。
ReactNative
通信流程(OC)
- ①js调用OC模块暴露出来的方法
- ②把调用方法分解为ModuleName、MethodName、arguments,在丢给MessageQueue处理
- ③把js的callback函数缓存在MessageQueue的一个成员变量里面,同时生成一个CallbackID来代表callback;在通过保存在MessageQueue的模块配置表把ModuleName、MethodName转成ModuleID、MethodID
- ④把ModuleID、MethodID、CallbackID和其他参数传给OC(JavaScriptCore)
- ⑤OC接到消息,通过模块配置表拿到对于的模块和方法
- ⑥RCTModuleMethod对js传过来的参数进行处理
- ⑦OC模块方法执行完,执行block回调
- ⑧调用第6步中RCTModuleMethod生成的block
- ⑨block带着CallbackID和block传过来的参数去掉用js里的MessageQueue方法invokeCallbackAndReturnFlushedQueue
- ⑩MessageQueue通过CallbackID找到相应的js的callback方法
- ⑪调用callback方法,并把OC带过来的参数一起传过去完成回调
优势
虽然不能做到一次编码到处运行,但是基本上即使是两套代码, 也是相同的jsx语法, 使用js进行开发。用户体验高于html, 开发效率较高
Flexbox布局据说比native的自适应布局更加简单高效
劣势
对开发人员要求较高,不是懂点web技术就行的,当官方封装的 控件、API无法满足需 求时就必然需要懂一些native的东西去 扩展,扩展性仍然远远不如web,也远远不如直 接写Native Code。
Weex
实现原理
Weex 对外通过 Rax 和 Vue 前端框架进行功能输出,前端框架下有一层 JS Framework 来实现 dom 的功能。 WeexCore 负责基础的 Flex Layout ,然后通过 Component 分别对接到 Android/iOS 的 Platform Native View 体系。
优势
- Android Native 采用更轻量级的渲染流水线,能更快更高效的的响应事件;
- RenderThread 直接操作 OpenGLContext ,进行 Direct GPU Raster ,充分发挥现代 GPU 的特性,提供高性能渲染和流畅的体验;
- 部分耗时操作,如 Bitmap 上传 Texture , TextureThread 上传到 Share Open GL Context 中, Texture 完成后通知主线程进行绘制,通过 Share Open GL Context 与主线程共享 Texture 等资源。 WebView 只能在 Render Process 内部进行 Texture 的共享, RenderProcess 无法与 GPU Process 共享 Texture 等资源;
- Android Native 有 RecycleView ViewPager 等高级组件,每个高级组件都做了性能的最佳实践;浏览器上的高级组件只能通过 JS 模拟实现,优化定制效率低;
- 浏览器流水线设计复杂,需要考虑到 PC 、手机、嵌入式设备等多种复杂的环境,不少设备上木有 GPU ,只能进行 CPU 渲染。无法像 Android Native 体系一样进行 All In GPU 的体系设计,全面发挥现代 GPU 的性能。
劣势
Weex 体系充分将 Native 的 View 体系输出到前端体系中,在进行 Android/iOS Native View 的封装过程中,存在不少难以逾越的障碍
Flutter自绘引擎
Flutter是Google发布的一个用于创建跨平台、高性能移动应用的框架。Flutter和QT mobile一样,都没有使用原生控件,相反都实现了一个自绘引擎,使用自身的布局、绘制系统
框架
基础架构主要分为三个部分:
Framework
- 纯 Dart实现的 SDK,类似于 React在 JavaScript中的作用
- 它实现了一套基础库, 用于处理动画、绘图和手势
- 基于绘图封装了一套 UI组件库
- 根据 Material 和Cupertino两种视觉风格区分开来
Engine
- 纯 C++实现的 SDK
包括
- Skia引擎
- Dart运行时
- 文字排版引擎等
- 它是 Dart的一个运行时,它可以以 JIT 或者 AOT的模式运行 Dart代码
- 这个运行时还控制着 VSync信号的传递、GPU数据的填充等,并且还负责把客户端的事件传递到运行时中的代码
Embedder
- Embedder是操作系统适配层
实现了
- 渲染Surface设置
- 线程设置
- 平台插件等平台相关特性的适配
渲染流程
- GPU的VSync信号同步给到UI线程
- UI线程使用Dart来构建抽象的视图结构(这里是Framework层的工作)
- 绘制好的抽象视图数据结构在GPU线程中进行图层合成(在Flutter Engine层的工作)
- 然后提供给Skia引擎渲染为GPU数据,最后通过OpenGL或者 Vulkan提供给 GPU
优势
高生产效率。一套代码可以开发出Android和iOS应用;Dart语 言优越性,使得同样的 功能只需要很少的代码;迭代更加方便, hot reload功能;
创建优雅的、高度可定制的用户界面。Flutter内置了对Material Design和Cupertino(iOS-favor)的UI组件库;提供了可定制 的UI组件,不再受制于OEM控件的限制;
借助可移植的GPU加速的渲染引擎以及高性能本地ARM代码运行 时以达到跨平台的高质量用户体验。
劣势
Flutter采用Dart语言开发,属于小众语言,需要一切都要重新 学习。
横向对比
对比内容 | ReactNative | weex | Flutter | Hybrid |
---|---|---|---|---|
平台实现 | JavaScript | JavaScript | 原生编码 | H5 |
引擎 | JSCore | JS V8 | Flutter Engine | Webview |
核心语言 | React | Vue | Dart | JavaScript |
打包bundle文件 | 默认单一文件比较大(可拆包) | 较小,多页面多文件 | 不需要 | 前端JS、CSS一般CDN引用 |
跨平台 | 中 | 中 | 中上 | 上 |
热更新 | 好 | 好 | 暂无方案 | 好 |
性能 | 中 | 中 | 中上 | 差 |