App 容器,简言之,App 承载某类应用(H5/RN/Weex/小程序/Flutter ...)的运行环境,可主动干预并进行功能扩展,达到丰富能力、优化性能、提升体验的目的,如页面数据预取(prefetch)缩短页面可用耗时、WebAR 将 AR 能力赋予 H5、Native 地图与 H5 复合渲染交互。
本篇主要就 H5 容器(WebView)相关建设进行概要展开。
我们先来做一个类比,通过 H5 的视角简要看一看 Android、iOS,进而更容易理解 WebView 容器建设机理。
本篇主要就 H5 容器(WebView)相关建设进行概要展开。
我们先来做一个类比,通过 H5 的视角简要看一看 Android、iOS,进而更容易理解 WebView 容器建设机理。
内容 H5 Android iOS
窗口 Window PhoneWindow UIWindow
页面控制 Html Activity UIViewController
内容区 body contentView(DecorView) view
常规 UI(容器、文本、图片) div、content、img View,TextView、ImageView UIView、UILabel、UIImageView。
通过如上对比可知,Native 与 H5 有很多相通之处的,如:H5 是一个 html 创建一个页面;Android 是一个 Activity 创建一个页面;iOS 是 UIViewController 创建一个页面。不同处在于,
Native 本身有完善的页面栈管理机制,在同一个 runtime 环境里控制页面间转换;还可以管理多个窗口(Window),有多线程/进程(仅 Android)辅助合理使用资源保障主线程/进程性能,是 App 的体验。而 H5 本身受运行环境限制,只能在一个窗口里活动,目前缺少同一个 Runtime 内成熟的页面栈管理机制,当前 SPA 方式切换 view 来模拟“页面转场”,已是 WebApp 体验的一种较佳实现了。所以,在 App 里,H5 期望能借助更多的 Native 能力。
在 App 里,是通过 WebView 来访问 h5 页面的,先来看一下 WebView 的官方释义:
A View that displays web pages. This class is the basis upon which you can roll your own web browser or simply display some online content within your Activity. It uses theWebKit rendering engineto display web pages and includes methods to navigate forward and backward through a history, zoom in and out, perform text searches and more.
其实,WebView 在 Android/iOS 两端的实现,都是继承自其 View/UIView 基类。对于 Native 原生来说,WebView 本身通过加载 h5 页面、通过 Chromium/WebKit 内核解析并进行 UI 合成,生成 view,Activity/UIViewController 实例的 View 通过 addView(Android)/addSubview(iOS)将 web view 添加进视图层,UI 合成,然后上屏展示。
我们知道,App 可以使用系统能力,但 WebView(类比浏览器)出于安全等考虑,默认并不提供。我们建设容器的一个主要目的是将 App 这些可用能力开放给容器。下面就谈一谈这部分能力是如何提供出来的,也就是下面要讲的桥通道相关建设。
网络优化
原生 Native 之所以体验平滑,有一关键点,是其页面依赖的静态资源大部分已打进安装包,跟随 App 的安装到了用户本地,节省了网络 IO 开销。顺着这个思路,在 5G 真正平民化之前,网络 IO 消耗,仍是提升性能的一个优化点,这块简要谈一下思路。
主要是两方面:
静态资源,html/js/css/图片/字体/视频等,通过离线化、预加载、懒加载、开启 WebView 缓存复用等进行文件获取前置备用或复用,当前用户访问页面加载资源时,容器拦截资源网络请求,命中离线或缓存的资源文件并使用。离线化与预加载可直接节省首次网络 IO 性能消耗,其他方式可以节省二次网络 IO 性能消耗。
离线化:即像 Native 一个打进 App 安装包内
预加载:App 启动后,在页面使用前,提前加载资源到用户本地备用
接口数据预取,选择合适的时机在用户访问页面前将接口数据获取到,当用户进入页面时,拦截接口网络请求直接命中本地缓存数据并使用。
这层优化,需要有配套的端远程控制机制与管理后台。合理设计总控机制,管控静态文件的发布、版本/patch 更新、下线等,以及接口数据预取的匹配规则、生命周期管理、静态/动态参数配置处理等。
WebAR
在说 WebAR 之前,先看一下 AR。简要讲:AR 是通过启动摄像头获取现实环境,以视频帧的形式将现实环境数据传输给 识别模块 与 绘制模块,识别模块将识别结果数据传递给绘制模块(根据业务配置的识别规则处理成对应的虚拟事物),绘制模块将 现实环境数据与识别结果数据处理后进行展现。
WebAR 是两个概念,是 Web + AR,在 Web 端提供 AR 能力。Web 自身,依靠 WebGL 实时图形渲染 与 WebRTC 实时视频流处理能力,能够实现 AR 体验。但受其运行环境标准不一、渲染性能差等因素影响,我们在容器侧进行了能力干预支持,以保障其功能与性能体验等。
在容器侧建设上,将 AR 能力集成到 App 上,再将 AR 能力提供给 WebView 使用(结合上面讲到的桥通道能力理解),以 H5 页的形式呈现。我们 Android 侧接入的是 UCWebView 提供的 AR 能力,使用 WebGL 渲染;iOS 是集成的 ARKit,通过全屏的 OpenGLView 上放置一个背景透明的 WebView 实现的混合渲染。
两端提供了配置识别模块能力支持。目前识别主要分为两类:一类基于 Maker 的识别(此类是常用的,识别标记图),另一类是基于位置的识别(通俗理解是通过手机传感器的方向和位置增强沉浸感,如位置远显示的内容较小,反之则大)。
当前小程序正如日中天,其有一项能力是很好的结合了 Native 与 H5,即“同层渲染”,下面我们也“纸上谈兵”一次。不过,我们也已有业务诉求,要做同层渲染的建设,将 Native 的 Video 等原生组件应用到 WebView 上满足业务体验需要。
同层渲染
这里讲的同层渲染,是将 Native view 合成到 WebView 上,可以通过 CSS 控制 Native View 在页面的样式。
以下介绍的两端方案,是基于目前笔者了解到的实现方案里最佳的。
Android
需要基于 Chromium 内核扩展自研的 WebView。Chromium 支持 WebPlugin 机制,用来识别解析 dom 标签。其思路:
html 里创建 dom 节点,指定组件类型,供容器识别处理
Chromium 创建 WebPlugin 实例,并生成 RenderLayer,其作用是创建独立的层并返回相应的画布供视图绘制使用
Android 根据识别的 组件类型 ,初始化一个对应的原生组件
Android 将原生组件的视图绘制 到 RenderLayer 所绑定的 SurfaceTexture 上(将 Android 的 UI Toolkit 的视图的数据送到 Texture 供 openGL 绘制使用)
Chromium 将此 RenderLayer 与 Web 页面的 View 进行合成渲染
iOS
基于 WKWebView,WK 在内部采用的是分层的方式进行渲染,一般会将多个 dom 节点,合并到一个层上进行渲染。因此,dom 节点和层之间不存在一一对应关系。但是,若将一个 dom 节点的 CSS 属性设置为 “overflow: scroll” 后,WKWebView 便会为其生成一个 WKChildScrollView,且 WebKit 内核已经处理了 WKChildScrollView 与其他 dom 节点之间的层级关系,这时 dom 节点就和层之间有一一对应关系了。所以,同层渲染可基于 WKChildScrollView 实现:
1.html 里创建 din 节点并设置其 CSS 属性为 overflow: scroll,指定组件类型,供容器识别处理
2.iOS 查找到该 dom 节点对应的原生 WKChildScrollView 组件
3.iOS 根据识别的 组件类型 ,初始化一个对应的原生组件
4.iOS 将原生组件挂载到该 WKChildScrollView 节点上作为其子 view,这样原生组件就被插入到了 webView 上
5.WebKit 完成渲染