1 跨平台
1.1 跨平台概念
1.1.1 概念
跨平台概念是软件开发中一个重要的概念,即不依赖于操作系统,也不依赖硬件环境。一个操作系统下开发的应用,放到另一个操作系统下依然可以运行。相对而言如果某种计算机语言不用修改代码即可做到高度跨平台,那么此语言就越抽象,硬件控制力就越低,只适合开发高度抽象的模型系统。诸如PHP和易语言,都已做到了跨平台。它们将可以在多种系统下开发,运行和维护。
1.1.2 目的
一套代码写完,在不同操作协调。不同硬件下能够正常运行,跨平台的目的是只写一套代码。
1.1.3 计算机组成、OS与开发者关系
真正工作者是硬件,硬件和语言面向的是OS,开发者面向的是语言
1.1.4 软件执行过程
一:硬件安装过程中,通过主板进行电路集成,确保物理层面的数据传输能够正常进行
二:注册驱动是告诉OS内核某个硬件的数据传输标准
三:当前APP写完后是一组固定的二进制字节,对应CPU的指令集或者GPU指令集
四:图形库中的GPU加速,实际上是代码执行指令推送到GPU硬件执行
图例:
1.2 语言的跨平台
语言的跨平台实际上就是适配CPU指令集,确保你的代码能在硬件上执行
1.3 UI跨平台
UI实际上不存在跨平台一说,这里所谓的跨平台实际上涉及到的是图形的生产与运行。以web为例,web底层是利用图形库实现的图像数据产生,Java的swing也是调用底层进行图像数据的生产,C的QT也是同理,只不过WEB做到的事情是将控件标准化定义,HTML+CSS目的是做到抽象一层统一的UI绘制标准出来。
2 跨平台实现流派
2.1 Web流
Web流也被称为Hybrid技术,它基于Web相关技术来实现界面及功能。
2.1.1 代表
PhoneGap/Cordova
2.1.2 实现原理
依托于web引擎的渲染来实现UI,底层需要的是一个web引擎来实现。主要依赖于浏览器引擎相关实现,规范按照HTML+CSS,运行载体是浏览器。
2.1.3 优缺点
性能慢,成本低。
2.2 代码转换流
将某个语言转成Objective-C、Java或C#,然后使用不同平台下的官方工具来开发
2.2.1 代表
Google Inbox、j2objc、MyAppConverter、SharPen、JUniersal、Haxe
2.2.2 原理与实现
写Native代码是必须的,但不同平台下的官方语言不一样,这会导致同样的逻辑要写两次以上,于是就有人想到通过代码转换的方式来减少工作量,比如将Java转成Objective-C。
2.2.3 优缺点
这种方式成本和风险都是最小的,因为代码转换后就可以用官方的各种工具,和普通开发区别不大,因此不用担心遇到各种诡异问题,不过需要注意生产的代码是否可读,不可读的方案就别考虑。但对于很多小APP来说共享不了多少代码,因为这类应用大多数围绕UI来开发,大部分代码都是UI耦合,所以公共部分不多,在目前所有具体方案中,只有j2objc可以尝试,其他都不成熟。
2.3 编译流
编译流比前面的代码转换更进一步,它直接将某个语言翻译为普通平台下的二进制文件,生成动态库或者打包程apk/ipa/xap文件
2.3.1 原理
采取开发静态编译器的方案,直接跳过上层语言的干扰,选择直接适配底层指令集
2.3.2 方案思考
选择C++做普通逻辑代码开发
使用 C++ 实现非界面部分,除了能提升性能和共用代码,还有人使用这种方式来隐藏一些关键代码(比如密钥),如果你不知道如何构建这样的跨平台项目,可以参考 Dropbox 开源的 libmx3 项目,它还内嵌了 json 和 sqlite 库,并通过调用系统库来实现对简单 HTTP、EventLoop 及创建线程的支持。
原因
1、MAC/Winodws/linux都支持C/C++的运行
2、但是思考,常规普通代码没有太大的问题,但是对于图形界面的支撑,在 iOS 和 Windows Phone 下可以分别使用 C++ 的超集 Objective-C++ 和 C++/CX,所以还比较容易,但在 Android 下问题就比较麻烦了,主要原因是 Android 的界面绝大部分是 Java 实现的,所以用 C++ 开发界面最大的挑战是如何支持 Android,这有两种做法:通过 JNI 调用系统提供的 Java 方法或者自己画 UI。
那自己画 UI 是否会更方便点?
1、UI风格问题考虑,google推的是 MD
2、APPLE 扁平化设计与拟物化设计,自己写带来的效果就是在风格上不一致会导致你图形开发需要有多套风格来支撑
3、要完全实现一遍 Android 的 UI 架构成本太大。
4、其中光是文字渲染就非常复杂,如果你觉得简单,那只能说明你没看过这个世界有多大,或许你知道中文有编码问题、英语有连字符(hyphen)折行,但你是否知道繁体中文有竖排版、阿拉伯文是从右到左的、日语有平假名注音(ルビ)、印度语有元音附标文字(abugida አቡጊዳ)……?
5、而相比之下如果每个平台单独开发界面,看似工作量不小,但目前在各个平台下都会有良好的官方支持,相关工具和文档都很完善,所以其实成本没那么高,而且可以给用户和系统风格保持一致的良好体验,所以我认为对于大多数应用来说自己画 UI 是很不划算的。
2.3.3 优缺点
优点:
1、可以重用一些实现很复杂的代码,比如之前用 C++ 实现的游戏引擎,重写一遍成本太高
2、编译后的代码反编译困难
3、或许性能会好些(具体要看实现)
缺点:
1、如果这个工具本身有 Bug 或性能问题,定位和修改成本会很高
2、编译后体积不小,尤其是如果要支持 ARMv8 和 x86 的话
3、UI风格上不统一
2.4 虚拟机流
2.4.1 说明
通过将某个语言的虚拟机移植到不同平台上来运行。除了编译为不同平台下的二进制文件,还有另一种常见做法是通过虚拟机来支持跨平台运行,比如 JavaScript 和 Lua 都是天生的内嵌语言,所以在这个流派中很多方案都使用了这两个语言。不过虚拟机流会遇到两个问题:一个是性能损耗,另一个是虚拟机本身也会占不小的体积。
2.4.2 介绍
Java 系
说到跨平台虚拟机大家都会想到 Java,因为这个语言一开始就是为了跨平台设计的,Sun 的 J2ME 早在 1998 年就有了,在 iPhone 出来前的手机上,很多小游戏都是基于 J2ME 开发的,这个项目至今还活着,能运行在 Raspberry Pi 上。
前面提到微软提供了将 Objective-C 编译在 Windows Phone 上运行的工具,在对 Android 的支持上我没找到的详细资料,所以就暂时认为它是虚拟机的方式,从 Astoria 项目的介绍上看它做得非常完善,不仅能支持 NDK 中的 C++,还实现了 Java 的 debug 接口,使得可以直接用 Android Studio 等 IDE 来调试,整个开发体验和在 Android 手机上几乎没区别。
另外 BlackBerry 10 也是通过内嵌虚拟机来支持直接运行 Android 应用,不过据说比较卡。
不过前面提到 C# 和 Java 在 iOS 端的方案都是通过 AOT 的方式实现的,目前还没见到有 Java 虚拟机的方案,我想主要原因是 iOS 的限制,普通 app 不能调用 mmap、mprotect,所以无法使用 JIT 来优化性能,如果 iOS 开放,或许哪天有人开发一个像微软那样能直接在 iOS 上运行 Android 应用的虚拟机,就不需要跨平台开发了,大家只需要学 Android 开发就够了
2.4.3 原理
虚拟机流一般采取的形式是代码呈现于物理文件存储,系统运行起来是一个C应用(基本所有OS都支持C),然后这个应用启动后完成代码的读取,转换,推送,与执行
2.4.4 优缺点
优点:实时的指令处理,类似代理的行为保证了适配的绝对
缺点:额外的内存消耗与运算处理
2.5 跨平台适配的主要方向提炼
2.5.1 语言
主要为了适配一次代码编写
2.5.2 界面
主要为了适配不同风格的UI
2.5.3 UI渲染引擎
生成图像数据
2.5.4 图像数据推送
3 常见跨平台方案实现原理
3.1 H5 APP
3.1.1 原理
Web 渲染方案主要是使用原生 WebView 控件渲染 HTML 页面,并在原生应用中定义可供 H5 页面访问原生部分能力的接口 JSBridge,从而实现 H5 和 Native 双向通信,也使得 H5 的能力向端侧进一步扩展。
Web 渲染方案本质上是依托原生应用的内嵌浏览器控件 WebView 去渲染 H5 页面,因此 h5 App 的渲染流水线和 Web 页面渲染相一致,能力也局限在 WebView 这一沙箱。下图描述从 WebView 初始化到 H5 页面最终渲染的全过程。
从上图上看,Web 渲染方案的性能瓶颈和 Web 页面开发中遇到的类似,即首屏渲染优化问题,同时多出了一个 WebView 初始化的特有问题。
对于 WebView 初始化所带来的性能开销,不少公司针对自身的 APP 进行内核的定制化改造,诸如腾讯的 X5 内核以及阿里 UC 技术团队的 UCWebView 等。
针对资源加载所带来的白屏问题,业界又提出了离线包的优化方案。所谓离线包机制,大体思路就是将原有从线上加载 H5 应用,提前下发到本地,通过 FileIO 或是内存等方式直接进行页面渲染,达到接近原生的用户体验。
上面所描述的是最为原始的 Web 渲染方案,在这基础上业内又提出 h5 容器的技术解决方案,h5 容器提供丰富的内置 JSAPI,增强版的 WebView 控件以及插件机制等能力,对原始版本的方案做了进一步功能高内聚和模块低耦合。
3.1.2 优缺点
1、开发效率高
采用 Web 技术,技术门槛相对较低,技术人员积累丰厚,社区资源丰富,对前端友好,一次开发,多端运行
2、动态化好
Web 技术的天然动态特性支持,无需发版
3、表现一致性佳
Web 页面除了个别元素和属性的差异、多屏适配外,其双端表现相对一致
4、性能较差
页面采用 WebView 渲染,页面加载耗时长,功能受限于沙箱,能力有限,难以承接复杂交互或是需要高性能的任务,整体用户体验差
3.2 RN
3.2.1 原生渲染方案
Web 渲染方案的致命弱点在于无法出色地完成高性能和体验的目标,但是其良好的社区生态、跨平台一致性和高研发效率都是其无法忽视的优势,那么如何做到二者的平衡,答案就是原生渲染方案。
原生渲染方案的基本思路是在 UI 层采用前端框架,然后通过 JavaScript 引擎解析 JS 代码,JS 代码通过 Bridge 层调用原生组件和能力,代表的框架是 React Native 和 Weex。
从原生渲染方案的实现思路不难看出,顶层采用类 Web 框架用于降低开发成本和统一技术栈,UI 的渲染通过 JSBridge 由原生控件直接接管,从而获得性能和体验的提升。
3.2.2 原理
一:React 层
最顶层是 React 层,利用 React 框架进行 UI 的数据描述,开发者使用 Class Component 或 Functional Component 进行页面开发,框架内部将会把页面描述转化为 ReactElement 这一代表的虚拟 DOM 的数据结构,用于运行时的 Diff 对比和消息收发等
二:JS Bundle 中间产物
React Native 通过 metro 打包功能直接将整个 RN 应用打包为一个 JSBundle,通过 Bridge 层在 RN 应用初始化时加载整个 JS 包进来
三:Bridge 层
Bridge 是连接 React 和 Native 的中间层,React 层的 UI 需要通过 Bridge 层的 UIManager 接口实现原生控件的创建和更新,通过 NativeModules 接口实现原生能力的调用
四:Native 层
在 Native 层中,Native Modules 实现了与上层交互的原生能力接口,Native UI 实现终端实际的控件展示,Yoga 跨平台布局引擎实现了基于 Flexbox 布局系统的 JS 和 Native 的镜像映射关系
3.2.3 说明
RN 应用在 UI 线程进行初始化,初始化的内容包括加载 JSBundle、初始化 Native Modules 等原生能力模块、创建 JSC/Hermes JavaScript 引擎,执行 JS 代码。
创建的 JS 引擎独立在一个 JS 线程,解释执行 React 代码,并将生成的布局或逻辑信息序列化后经由 Bridge 发送给 Native。
React 代码中视图层的渲染通过 UIManager 调 createView/updateView 等方法,基于 Yoga 布局引擎创建对应的 shadowView;逻辑层中涉及原生能力调用的部分通过 RCTBridge 对象转发到相应的原生接口。Native 接收到 Bridge 层的消息,进行视图的更新或是功能处理。
3.2.4 优缺点
原生渲染方案通过直接接管渲染层的方案,弥补了 Web 渲染方法在性能和体验上的不足,同时在顶层采用类 Web 的语法集,将开发技术边界延展至 Web 领域,同时可以很好的复用当前前端主流 UI 框架 React/Vue 的繁荣生态系统。
虽然原生渲染方案有上述的优势,但是有一个致命的弱点就是 Native 层和 JS 层的通信所带来的性能瓶颈。一方面页面的更新和事件的响应经由 Native 触达 JS 层,再由 JS 层返回给 Native 层需要来回的时间成本,另一方面数据的交互需要频繁进行序列化和反序列化的转换。因此,在一些 UI 线程和 JS 线程存在持续频繁交互的场景(动画、滚动)等,RN 表现就不尽如人意