AIGC在金融场景是如何落地的? 了解详情
写点什么

携程酒店 iOS 动态 View 的探索

  • 2019-06-13
  • 本文字数:3640 字

    阅读完需:约 12 分钟

携程酒店iOS动态View的探索

一直以来,Native App 因为审核的原因,新版本不能很及时地上线。尤其是 iOS,碰到点审核问题,有时候一连几天都不能上架,严重影响业务和产品的体验。


大家一直都在寻求能够动态更新业务的方法,关于这方面的框架也是层出不穷。自从 Facebook 推出 React Native 以后,便以其良好的兼容性和性能优势占据了这方面的领先地位,携程也在此基础上开源了CRN框架


如果是新业务,用 CRN 开发是非常合适的,开发效率高,双平台兼容性好。但如果要把已有的 Native 页面转 CRN,复杂的核心页面成本会有点高。在不增加人手的情况下,要想同时进行业务的迭代和 CRN 的转换,会有点力不从心。


如果硬转,周期会很长。以携程酒店主流程页面之一的订单详情页为例,在没有额外增加人手的情况下,前后花了几个月时间,才陆陆续续完成了 90%的功能转 CRN,过程尤为艰辛。订单详情页是主流程页面中相对简单的,如果要转酒店详情页,光是几百行的 ViewModel 就已经让人望而却步了。


对此,我们考虑能不能采用一种让 Native 和 CRN 共存的方式,这样既可以保留 Native 的业务逻辑,又可以在 UI 层面做到灵活应变。最关键的是,可以分模块的开发,而不用像转 CRN 那样必须整个页面一起上。


当然,Native 和 CRN 混合的解决方案早就有了,但是当 CRN 作为一个子 View 出现在 Native 页面里的时候,由于 CRN 的框架比较重量级,在性能上并不是特别理想,而且和 Native 的交互也不是特别方便,所以我们开始考虑有没有更为轻便的解决方案


在比较了多种跨平台方案之后,首先排除了类似 Lua 这种需要依赖第三方库,且语法非主流的方案,最终决定采用原生系统就自带支持的,且语法有着广泛群众基础的 JavaScript。


在 iOS7 之前,要在 Native 环境中和 JavaScript 交互是非常简单且功能有限的,基本上只有依靠 Webview 的 EvaluateJavaScript 来注入执行一段 JS 脚本。从 iOS7 开始,苹果引入了 JavaScriptCore 这个库,顿时给 iOS 的开发带来了翻天覆地的变化。


为什么会这么说呢,首先来看一下 JavaScriptCore 中所包含的两个关键类,JSContext 和 JSValue:


JSContext


JSContext 提供了一个在 APP 中执行 JavaScript 代码的环境,使得我们可以直接在 Objective-C 或 Swift 代码中直接调用 JavaScript 代码,并得到返回结果,反过来也可以暴露方法和类供 JavaScript 调用。


JSValue


JSValue 则是一个 JavaScript 数据类型在 Objective-C 或 Swift 中的包装对象,借助于这个对象我们可以在 Native 代码和 JavaScript 代码之间互相传值,这两者之间的对应关系如下图所示:


Objective-C (and Swift) TypesJavaScript Types
nilundefined
NSNullnull
NSString (Swift String)String
NSNumber and primitive numeric typesNumber, Boolean
NSDictionary (Swift Dictionary)Object
NSArray (Swift Array)Array
NSDateDate
Objective-C or Swift object (id or AnyObject)
Objective-C or Swift class (Class or AnyClass)
Object
Structure types:
NSRange, CGRect, CGPoint, CGSize
Object
Objective-C block (Swift closure)Function


简单总结一下,JSContext 提供 JavaScript 和 Native 互相调用的接口,JSValue 提供互相调用之间的数据类型转换,这样的调用方法比之前的 Webview 要强大灵活许多,想象空间也大了很多。所以我们接下去就准备在这基础之上做点文章。


第一步,先创建一个 JavaScript 对象,用来描述对应 iOS 中的 UIView,代码用 ES6 如下:


Class View {    constructor() {         this.x = 0;         this.y = 0;         this.width = 0;         this.height = 0;         this.borderWidth = 0;         this.borderColor = ‘’;         this.cornerRadius = 0;         this.masksToBounds = false;         this.subviews = [];     }     initWithFrame(x, y, width, height) {        ……     }     addSubview(v) {        ……        }     setOnclick(click) {        ……        }     ……}
复制代码


这些属性和方法都是 iOS 中 UIView 比较常用的,如同在 iOS 中 UILabel 是继承自 UIView 一样,我们继续创建一个 JavaScript 的 Label 对象,并继承自刚才在上面创建的 View 对象。


Class Label extends View {    constructor() {         super();         this.text = ‘’;         this.textColor = ‘’;         this.textSize = 14;         this.fontStyle = 0;         this.textAlignment = 0;         this.lineBreakMode = 4;         this.numberOfLines = 1;    }    }
复制代码


以此类推,我们继续创建诸如 Imageview,Button,ScrollView 等 iOS 中常用的组件,只要愿意,所有的组件都可以用这种方式来描绘。


有了这些基础的 JavaScript 组件,接下去就可以如同在 iOS 中布局一样,开始用这些组件进行布局,如下代码片段示例了如何对一张图片进行布局。


createImage() {       var container = View.initWithFrame(0, 0, 50, 50);       container.backgroundColor = "#FFFFFF";       var image = Image.initWithFrame(0, 0, 50, 50);       image. imageUrl = ‘http://m.ctrip.com/xxxxx.png’;       container.addSubView(image);       return container; }
复制代码


对于熟悉 iOS 开发的同学来说,会觉得这段代码非常眼熟。没错,这就是一段用 JavaScript 来写的 iOS 代码,依此类推,稍微复杂一点的布局也可以用这种方式完成。


最后来看一下布局完成以后的返回值,暂时还是先以上面的 Image 控件来做示例:


        varcontainer = View.initWithFrame(0, 0, 50, 50);       container.backgroundColor = "#FFFFFF";       var image = Image.initWithFrame(0, 0, 50, 50);       image. imageUrl = ‘http://m.ctrip.com/xxxxx.png’;       container.addSubView(image);        var demoView = View.initWithFrame(0,0,180,180);       demoView.addSubView(container)       return demoView;}
复制代码


如果在浏览器或者 JavaScript 环境中运行上述代码,会得到一个自定义的递归对象,根对象会包含一个 Subview 数组,数组中的每个元素都有可能是另外一组 UI 对象,当然实际操作中并不建议层次太多,一般 1-2 层。


做到这里,JavaScript 的部分暂告一段落。接下来回到 Native 当中,还记得上文提到的 JSContext 么?这是一个在 Native 当中的 JavaScript 执行环境,我们在 Native 环境中用 JSContext 来执行刚才那个 Demo,就会得到一个对应的 JSValue 值,这个 JSValue 的值用[JSValuetoObjct]来转换成 Object-C 对象的话,最终就得到了一个字典,NSDictionary。


继续递归地拆解这个字典,拆解到底,每个元素最终都会转成 OC 的 Object,然后根据每个 Object 预先定义好的 Type 类型,实例化成相应的 Native 组件,并且每个组件有一个对应的数据 Model。


还是以上述那个 Label 为例,其对应的 OC Label 代码如下:


@implementation Label - (void)setModel:(HTLDynamicLabelModel *)model{   self.dynamicViewModel = model;   self.text = model.text;   self.textColor = model.textColor;   self.font = model.font;   self.lineBreakMode = model.lineBreakMode;   self.numberOfLines = model.numberOfLines;       if(model.richText && model.richText.attributedString) {       self.attributedText = model.richText.attributedString;    }} @end
复制代码


到此为止,就完成了所有之前在 JavaScript 中描绘的控件在 Native 里的转换,剩下的事情就是对这些 Native 组件进行渲染了,具体就不在这里描述了。


总体来说,这个思路在原理上跟 RN 或者 CRN 是一样的,但更为轻量一点,几乎 0 配置就能使用。通过配置增量更新,从服务端下载最新的 JS 文件,可以做到类似 CRN 在线更新的效果。


从性能上来看,因为不需要额外加载任何框架代码,JS 执行的消耗几乎可以忽略,所以和 Native 混合在一起的时候,几乎看不出有任何延迟。


这个方案非常适合做一些轻量级的又需要经常不定期更新的 UI,比如节日氛围或者城市包装的 UI。这些 UI 经常会跟随节假日更新,用这个方案可以轻松在线更新 UI,不用通过服务端下发一堆样式来控制,减轻了服务发布的压力和不必要的服务交互。


综上所述,这是我们团队对新事物的一些探讨和研究,并不存在要代替 CRN 或其他框架一说,每个框架都有其适用的场景,没有绝对的优劣之分。


在研究这个解决方案的过程中,我们也认真地深入了解了 JavaScriptCore 的一些机制,原理都是万变不离其宗的,但可以结合不同的场景,进行不同的演变,就看怎么灵活运用了。


所以,与其说本文是在探索 iOS 中动态 View 的解决方案,也不妨说成是对 JSContex 和 JSValue 如何运用的一些探讨,从实际的摸索中来看,灵活运用好 JavaScriptCore,可以有无限多的可能。

作者简介

姜睿东,2009 年加入携程,从事无线研发,现在大住宿事业群负责酒店无线研发工作。


本文转载自公众号携程技术中心(ID:ctriptech)


原文链接


https://mp.weixin.qq.com/s/1N2ACBrOSR6mBsPYR-0CRg


2019-06-13 08:005695

评论

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

80+产品正通过兼容性测试,OpenHarmony生态蓬勃发展

OpenHarmony开发者

OpenHarmony

2021金三银四巨作!10万字!阿里高工纯手码JDK源码笔记 带你飙向实战

爱好编程进阶

Java 程序员 后端开发

985毕业,半路出家28岁进军Java,坚持了三年现如今年薪36W

爱好编程进阶

Java 程序员 后端开发

AOP实战篇 如何轻松实现日志功能,戳这

爱好编程进阶

Java 程序员 后端开发

使用 GoRouter 进行 Flutter 导航:Go 与 Push

坚果

4月月更

APICloud数据云3.0使用教程

YonBuilder低代码开发平台

后端开发 APP开发 APICloud 数据云

C语言总结_语句、运算符

DS小龙哥

4月月更

17 网站架构的伸缩性设计

爱好编程进阶

Java 程序员 后端开发

SAP 云平台(Cloud Platform)架构概述

Jerry Wang

云计算 云平台 SAP 云计算架构师 4月月更

5年Java后台经验程序员,历经6面,跳槽腾讯

爱好编程进阶

Java 程序员 后端开发

己酉篇 「反元富民」《「內元宇宙」聯載》

因田木

元宇宙

21年GitHub标星19

爱好编程进阶

Java 程序员 后端开发

@产品部 -- 腾讯策划部是如何培养用户的《王者荣耀

爱好编程进阶

Java 程序员 后端开发

架构实战营 - 总结

李大虾

#架构实战营 「架构实战营」

Docker下,pinpoint环境搭建

程序员欣宸

Java Docker 4月月更 Pinpoint

OpenHarmony加速行业应用落地,多款软件发行版正在通过兼容性测评

OpenHarmony开发者

OpenHarmony

替代 Elasticsearch,TDengine 助力四维图新将存储空间利用率提升 8 倍

TDengine

数据库 tdengine

OpenMLDB:线上线下一致的生产级特征计算平台

第四范式开发者社区

人工智能 机器学习 数据库 开源 特征

16 条 yyds 的代码规范

爱好编程进阶

Java 程序员 后端开发

2021金三银四吃透这份Java面试突击手册!成功跳槽涨薪30K

爱好编程进阶

Java 程序员 后端开发

(项目实战)如何结合k8s和pipeline的流水线,并通过k8s接口完成镜像升级

爱好编程进阶

Java 程序员 后端开发

2020最新互联网数据调查显示,Kotlin-势必取代-Java?

爱好编程进阶

Java 程序员 后端开发

ArrayList 与 LinkedList 的方法及其区别

爱好编程进阶

程序员 后端开发

丁未篇 「元宇宙超次元 」 《「內元宇宙」聯載》

因田木

Web3.0 命書

灵感乍现!造了个与众不同的Dubbo注册中心扩展轮子

捉虫大师

Java dubbo 注册中心 4月月更

电商秒杀系统架构设计

李大虾

#架构实战营 「架构实战营」

Tomcat:网络请求原理分析

IT巅峰技术

企业知识库建立的目的及意义

小炮

知识库

敏捷开发中如何从容应对需求变更?

华为云开发者联盟

敏捷开发 需求 软件开发 需求变更

20万+字,熬夜整理了一份程序员不可或缺的软技能高分原创电子书送给你

爱好编程进阶

Java 程序员 后端开发

30岁码农的一次面试经历:不委屈自己

爱好编程进阶

Java 程序员 后端开发

  • 扫码添加小助手
    领取最新资料包
携程酒店iOS动态View的探索_语言 & 开发_姜睿东_InfoQ精选文章