【AICon】探索RAG 技术在实际应用中遇到的挑战及应对策略!AICon精华内容已上线73%>>> 了解详情
写点什么

如何用游戏引擎改造 Hybrid App?

  • 2018-05-01
  • 本文字数:4105 字

    阅读完需:约 13 分钟

在移动设备上,Hybrid 混合开发的性能问题一直为人诟病,对它的各种优化、hack 层出不穷,但你听说过用游戏引擎来优化 Hybrid 性能的吗?这次我们的 GMTC 邀请到了腾讯高级工程师潘伟洲分享《基于 Cocos 的高性能跨平台应用开发方案》,我们提前对他进行了采访,了解了一些技术细节,分享给大家。

InfoQ:Cocos 作为开源的跨平台游戏引擎框架,是一个什么样的契机让你和你的团队想到了用 Cocos 来改造原本 Hybrid 形式的产品的?

潘伟洲:先从动力说起:ABCmouse 原来的版本出于跨平台的目的,项目的大部分采用的是 Hybrid 的形式,大部分的页面加载都非常耗时,以一个画图的功能为例,从打开到加载完成可能要耗费十几秒的时间,这对于大部分用户来说是难以忍受的。

通过初期技术预研后,我们决定使用 Cocos 来改造这个项目,主要出于以下几个考虑:

  1. 跨平台。Cocos 支持使用同一套代码构建生成 Web、iOS、Android 等几个端,最新的版本还支持发布到微信小游戏、Facebook Instant Games 和 QQ 玩一玩;
  2. 性能。Cocos 的原理是在 Activity 中绘制一个 OpenGL 的 SurfaceView ,并由其完成页面的渲染的。与基于 WebView 渲染的 Hybrid 应用相比,Cocos 的渲染速度更快,性能更好。
  3. 效率。借助可视化的 Cocos Creator 工具,界面的开发和资源的管理非常便捷,设计团队也可以参与进来设计界面和动效,提升开发效率。
  4. 表现力。ABCmouse 中包含了很多诸如游戏、画图、音乐等带游戏和娱乐性质的场景,而 Cocos 本身是为游戏开发设计的,更适合用在我们的产品中。

InfoQ:在实际开发过程当中,与其他跨平台的开发框架相比较,你觉得用 Cocos 的优势在哪里?

潘伟洲:首先是生成的平台更多,除了常见的 Web、iOS、Android 外,难得的还支持微信小游戏、Facebook Instant Games 和 QQ 玩一玩的发布。其次,因为 Cocos 的核心开发团队来自中国,能提供更好的技术支持。腾讯也是 Cocos 的合作伙伴,我们与他们建立了良好的合作关系。

InfoQ:使用 Cocos 开发,你们在技术选型,基础组件储备上做了哪些尝试与创新呢?

潘伟洲:技术选型方面,我们先是为 UI 层设计开发了一些 Cocos 所缺失的一些通用组件,包括:对话框、日期选择器、Toast、Loading 组件等,这些组件能在全部目标平台上通用,并且遵循着统一的接口风格,使得 UI 层的开发更加便利;

在图片加载方面,我们实现了一个带缓存功能的图片加载组件,优化二次加载在线图片的性能,并自动管理内存回收。另外, 为了提高图片的加载性能,我们改进了 Cocos 的图片的加载方式,让其支持使用 ETC2 纹理渲染,使得内存消耗降低了接近 70% 。

由于应用中有非常多的音乐、音效、语音,为了减小包大小,大部分的语音素材放在 CDN 上,需要的时候才从 CDN 上拉取播放。少部分常见的音效会直接打进应用包中。而 Cocos 自带的 AudioEngine 组件在 Native 端只支持本地资源的播放。因此,我们又封装了一个跨平台的音频播放器,可以自动根据指定的音频路径决定使用播放方式:

  • 对于 Web 端或者 Native 端的本地资源文件,直接使用 AudioEngine 来播放。
  • 对于 Native 端的远程音频,使用 Native 的播放器来播放。

由于对外的接口只有一套,开发者无需考虑具体的平台和底层播放器的选择。并且可以使用同样的接口来统一管理不同的音频。

由于客户端大部分的功能都是使用 JS 写的,为了更快速地定位问题,我们在底层对 JS Exception 做了拦截,一旦出现 JS Exception,就将错误信息通过 Toast 展示出来,并且详细的给出了堆栈信息和场景信息。这个错误展示帮助我们发现并修复了很多 JS 错误。

除了以上所提到的几个能力,我们在 Cocos 层还提供了通用的日志记录、事件上报等。这些在不同的端有不同的实现,但都通过 Cocos 层进行了适配,使得对开发者都是统一的一套接口。

InfoQ:在开发设计 ScrollView 控件时,你们做了哪些优化?

潘伟洲:官方 ScrollView 组件需要配合 layout 组件,当一次加载大量的子节点组件,或者分帧加载单个子节点组件时,初始化 ScrollView 节点视图会比较慢,在加载完成前存在拖动掉帧的问题。另外,一次性加载所有节点,也会导致内存资源的浪费。

我们对 ScrollView 进行了重写,基本的优化思路是:一次仅加载页面可容纳的少量数目子节点。并在滚动过程中,回收不可视的子节点组件并重用。

具体来说,ScrollView 大多数情况下表现为列表组件和宫格组件,以列表组件为例,可以根据子节点数目和子节点大小,计算出整个 ScrollView 内容的宽高,同时计算出屏幕可视区域最多可以容纳的子节点行数 rows,加载时仅加载 rows + 2 个子节点组件,其中添加的 2 个字节点组件作为滚动回收缓冲。举个具体点的例子,当手势向上,内容往下滚动时,一旦最上面一个子节点组件 A 不可视,就立马回收掉 A 并将其重用于将要渲染的子节点组件。

我们做过测试,我们有个页面加载了非常多的内容,在优化前,拖动的时候帧率可能会跌到 8 fps,而使用优化后的 ScrollView,帧率能够稳定在 60 fps。

InfoQ:在实际运用 Cocos 开发过程中,你们有踩过哪些坑呢?包大小的问题又是怎么解决的呢?

潘伟洲:我们遇到的第一个坑和 Cocos 的 VideoPlayer 组件有关:Cocos 的基本原理是在 Activity 里头展示一个 OpenGL 的 SurfaceView ,这个 SurfaceView 不能直接绘制视频。

为了解决视频的播放问题,Cocos 的引擎开发者们将视频播放器作为独立的一层 View ,并保持置顶。这带来的问题是:VideoPlayer 组件上无法绘制任何其他组件。比如,如果希望定制播放按钮、进度条等元素,使用 Cocos 的 VideoPlayer 是做不到的。

最终我们放弃了在 Cocos 层开发视频播放器的功能,而是在底层为各个端开发视频播放器,并各自实现界面的定制。

第二个坑是跨层调用的响应问题。Native 层和 Cocos 层交互时,存在一定的响应时间。

比如,前面说到,我们的视频播放器是在 Native 层开发的,我们有一个需求:当视频播放完后,立即跳转到另一个 Cocos 的场景。然而,由于跨层调用存在一定响应时间,当把视频播放器的 activity 关闭时,此时页面会回到调起视频播放器前的场景,然后才进入一个新的场景。

为了解决这个问题,我们在 Cocos 层设计了一个隐藏的常驻节点,在关闭 activity 前,先把这个节点设为可见,并将其颜色改为下一个场景的背景色,直到下一个场景加载完毕时才重新将该节点设为隐藏。由于设置节点的可见性远比加载场景要快,此时再关闭 activity 时,用户先看到的是这个节点,给用户一种即将进入下一个场景的错觉,就不会觉得场景的跳转很突兀。

最后我们遇到的一个比较严重的问题是 local reference table overflow error 问题。

为了复用 Native 端的能力,我们在 Cocos 层大量地使用反射机制来调用 Native 端提供的方法。然而,我们经常会遇到 local reference table overflow error 错误导致的界面卡死问题。最初,我们怀疑是反射调用使用得太频繁导致。因此,我们对诸如打 log、事件上报等 Native 方法进行了频率限制,例如使用缓冲的方法将多个 log 合并后再打印。

然而,虽然这个做法减少了界面卡死的发生,但依然没有彻底杜绝问题的再次出现,就像是一个定时炸弹一样,威胁着我们应用的稳定性。通过阅读引擎的代码,我们发现 Cocos 的引擎在反射阶段处理字符串参数时,使用了 NewStringUTF() 方法将其转换为 JNI 层的字符串,然而在调用执行完成后并没有相应地使用 DeleteLocalRef() 释放该字符串的引用,从而导致了引用表的溢出。了解到这个原因后,我们给 Cocos 的引擎提交了一个 pull request,修复了这个问题。

在包大小的控制方面,我们都知道图片资源往往是导致应用包臃肿的元凶。因此,为了减少包大小,我们应该能够找出工程里头的无用资源,以及可优化的资源。

针对无用资源,Cocos 建议将静态引用的图片存在 res 目录,而将动态引用的图片放在 resource 目录。这么区分的好处是,res 目录下的图片只有存在引用时才会被打包进应用,而没有被引用的图片则不会被打包进应用。

因此,我们首先编写了静态分析脚本,找出工程中不合理放置的图片资源(例如将静态引用的图片放置到 resource 目录的情况),并移除 resource 中没有使用的图片。另外,对于 res 目录,我们也会经常检查是否存在不必要的引用,比如移除废弃的场景或者节点等。

可以优化的资源指的是可以进一步压缩的资源,这个和原生应用的优化思路差不多。比如合理的使用有损压缩 / 无损压缩工具来压缩图片文件。对于不需要透明度的图片资源,则使用 JPEG 格式代替 PNG 格式。

另外,在实现一些动效时,我们也尽量使用属性动画而不是帧动画,以节省空间。在这一点上,Cocos 的优势比较明显。我们的动效通常是设计团队直接使用 Cocos Creator 的动画编辑器创建的动画剪辑,并非简单的帧动画。

InfoQ:在什么样的场景下,才适合做基于 Cocos 的产品开发呢?

潘伟洲:取决于产品的定位。Cocos 主要是为游戏开发设计的,所以产品的表现力更佳,但在性能、内存占用、耗电上不如普通原生应用。当你的产品要求有更高的表现力,UI 开发更灵活,并且希望能实现跨平台开发时,Cocos 是一个值得考虑的选择。

InfoQ:在众多的跨平台 app 开发框架当中,开发者如何找到适合自己的框架?

潘伟洲:建议结合产品的需求、定位、框架成熟度、社区支持情况等维度进行综合考量。

比如,如果你要开发一个视频播放器,那么像 Cocos 这种使用 OpenGL 绘制 UI 的框架就难以实现跨平台开发。而如果要开发一个高表现力的应用,比如内置小游戏或者是带翻书特效的阅读应用,那么就应该考虑 Cocos 这类支持 OpenGL 绘制的框架,或者与 H5 页面结合的 Hybrid 框架。

另外,如果产品对性能要求更高,那么应该选择更 “Native” 的框架,例如 React Native、Weex ,或者 Flutter 。而如果产品比较轻量,也更看重开发效率,那么可以选择 Hybrid 的框架。

除了从产品自身的需求定位出发,选择一个框架还应该考虑这个框架本身的成熟度以及社区支持情况。比如,Google 的 Flutter 是最近比较受关注的跨平台开发框架,它的亮点在于跨平台、高性能和不俗的表现力。然而这个框架目前还不够成熟,社区的轮子还不够丰富,能够得到的帮助也比较有限,所以建议现阶段可以保持关注,但小团队不要贸然去尝试。

感谢覃云对本文的审校。

2018-05-01 18:162678

评论

发布
暂无评论
发现更多内容

迪斯克Disrupt DEX众筹挖矿系统开发技术

薇電13242772558

dapp

算法题每日一练:最长递增子序列

知心宝贝

数据结构 算法 前端 后端 三周年连更

Qz学算法-数据结构篇(查找算法--线性、二分)

浅辄

数据结构 三周年连更

用友联合7家信创生态伙伴成立“信创领先实践联盟”,助力企业数智化国产替代

用友BIP

Gradio入门到进阶全网最详细教程[一]:快速搭建AI算法可视化部署演示(侧重项目搭建和案例分享)

汀丶人工智能

人工智能 机器学习 深度学习 Gradio streamlit

宠物行业如何做留存?答案都在这里了!

游读分享

如何建设一个用于编译 iOS App 的 macOS 云服务器集群?

京东科技开发者

ios 编译 CI/CD 企业号 4 月 PK 榜 云服务集群

代码优雅之道——如何干掉过多的if else

小小怪下士

Java 程序员 后端 代码

孔再华:openGauss在民生银行的应用实践

openGauss

黄凯耀:深度解读openGauss架构创新与新特性

openGauss

大数据之Hadoop图解概述

袁袁袁袁满

三周年连更

一些常见的字符串匹配算法

京东科技开发者

字符串 字符串匹配算法 企业号 4 月 PK 榜 文本处理

用友BIP助力中国领先企业数智化国产替代

用友BIP

国产替代

复旦MOSS大模型开源了!Github和Hugging Face同时上线

Openlab_cosmoplat

人工智能 开源项目 ChatGPT

使用AI优化慢SQL,开发秒变DBA

NineData

sql AI 开发者 dba NineData

沉默的618下,美妆品牌如何踏上破局之路?

游读分享

【Linux】之Centos7安装KVM虚拟化及相关命令

A-刘晨阳

Linux 虚拟化 kvm 三周年连更

Nginx常用配置及和基本功能讲解

京东科技开发者

nginx 负载均衡 正向代理与反向代理 企业号 4 月 PK 榜 Nginx入门

连接OpenAI两大智能产品Whisper & DALL·E,实现智能语音转文本/文本转图像

集简云开放平台

AI 智能 openai 数据集成平台 AI绘画

JUnit 5 参数化测试

码语者

Java JUnit

openGauss:共建数据库根社区,打造开源数据库核心竞争力

openGauss

Java如何获取@ApiModelProperty(value = “序列号“, name = “uuid“)注解中的value值name值?

bug菌

三周年连更 获取注解值

如何优雅的处理异常

京东科技开发者

异常处理 java 抛出异常 企业号 4 月 PK 榜 throwale

突破传统监测模式:业务状态监控HM的新思路

京东科技开发者

系统架构 监控系统 数据监控 业务监控 企业号 4 月 PK 榜

语雀-使用指南

六月的雨在InfoQ

在线文档 在线协同文档 三周年连更 语雀

流量即生意!红海中的茶饮品牌如何做增量?

游读分享

李士福:openGauss 自驾驶数据库内核在AI领域的探索和创新

openGauss

易观千帆 | 2023年3月证券APP月活跃用户规模盘点

易观分析

证券 经济

执行个 DEL 竟然也会阻塞 Redis?深挖一下果然不简单

架构精进之路

redis 缓存 后端 bigkey 三周年连更

再次!openGauss荣获中国软协产业协会多个奖项

openGauss

《API加速优化方案:多级缓存设计》

后台技术汇

三周年连更

如何用游戏引擎改造Hybrid App?_语言 & 开发_胡骁杰_InfoQ精选文章