个性化UI在金融场景的探索和应用

2020-8-3 评论(2) 分类:技术文章

背景

随着各业务流量红利逐渐见顶,如何在有限的流量下,提升流量给用户带来的体验,对流量进行精细化运营,进而提升转化效率,成为各业务的重要课题。

蚂蚁数字金融线包含了 保险/理财/信用/借贷 等业务,经过多年建设,形成了多样的金融资产/场景服务/权益/内容等多维度的产品去服务用户,而这些产品分发给用户时,决定曝转率的大体上以产品推荐是否精准,以及产品以什么样的UI表述方式进行展示决定。在产品推荐上,算法已经逐步覆盖,给业务带来很大的效果提升,在产品推荐算法优化到一定程度后,提升的投入产出比已经有限,我们开始在UI展示上探索,是否个性化智能化的展示能为业务带来提升。

探索

UI1

淘宝比较早地做了智能UI的尝试,在推荐出商品后,对于商品的UI展现方式进行千人千面个性化,商品列表有两列/三列/横排的布局方式,商品封面有 场景图/白底图/视频 等类型,商品本身有 标题/描述/评价/销量/属性/标签/活动/价格/快递/地区 等一系列字段标签,在列表上无法全部容纳,不同的人对不同的 布局密度/封面/字段标签 会有不一样的关注度,这里就有了智能化的空间,对这些属性进行自由组合千人千面推荐,取得不错的提升成果。

在数字金融线的业务,是否也可以这样做?数字金融线各业务的“货”并不是统一标准化的商品,主要由 金融资产/服务/权益/内容 构成。例如财富业务,金融资产(基金/定期等)在购买门槛高的情况下,会通过各种包装降低门槛,包括场景化包装成服务(工资理财/笔笔攒等),配合营销活动包装权益(体验金/财运金/黄金票等),包装资讯/视频/直播等形式的内容。同时这些包装后的“货”在不同场景有不同的UI展现形式,并不是标准化的,这些“货”也没有多种字段可以直接进行自由组合搭配,如何能做个性化千人千面?

问题

我们可以针对一个个场景设计多个模板样式去匹配不同的人,这也是之前在一些业务上进行过一些尝试的,但有几个问题:

  1. 无设计标准:设计什么不同的UI样式才可能有效,没有标准和指导,靠设计师个人经验。
  2. 样式生产效能:UI样式需要一定量级进行分人群匹配,设计工作量大。
  3. 算法匹配信息不足:算法难以识别不同UI样式间的差别,以 人<->样式ID 的方式匹配效果有限。
  4. 无沉淀不通用:不同的场景割裂,设计经验/算法经验/工程链路无法复用,无法沉淀用户偏好,各场景实现成本大。

针对这些问题,我们探索了一套相对通用的个性化UI解决方案。

方案

语义标签体系

UI2

首先是根据过往数金各场景的设计经验,由设计团队主导抽象出一套UI语义标签体系,去描述一个UI样式里可能吸引用户点击的关键点。

这个标签体系分表现层和内容层两个层面:

  1. 表现层去描述UI长什么样,比如它的风格是实物还是扁平的,色彩是鲜艳的还是偏透明的,字体相对大小。
  2. 内容层表示这个UI展现有什么内在含义,比如它是突出权益的(红包/抽奖等),代表从众心理的(xx人正在购买),代表认知的(黄金图/人物图/降维描述/品牌权威描述)等等。

我们认为不同人对这里列出来的标签是会有不同偏好的,例如年轻持仓少的,可能用扁平图/从众/降维描述相对能打中,老年人可能对权益/实物图形/大按钮比较敏感。

这套标签期望能产生几个作用:

  1. 指导设计往什么方向进行差异化设计。
  2. 语义化描述UI,作为特征让算法更好地认识UI的外在和内在含义,多场景通用。
  3. 后期可以针对线上数据进行归因分析,语义化看出不同用户的UI偏好差异。

工程算法

标签体系的思路把UI的维度拆细了,为不同人原子化设计不同的元素,我们沿着这个思路打造了相应的工程和算法方案:

UI3

1.多样式生成

我们将UI原子化打散-自由组合。一个产品可以以各种方式包装后呈现给用户,主要包括描述产品的创意物料(文案/图片/动画),再配合不同的样式和版式(字号/背景/布局排版)组成,这些元素可以进行自由组合,例如一个单图文模块,我们可以参考标签体系设计10个文案,10个图片,2个排版样式,排列组合就可以生成200个UI样式包,作为丰富的可以匹配不同用户的素材。

2.UI偏好模型

我们将组合生成的UI样式包结合UI标签与用户属性进行匹配关联,以CTR为目标训练模型。训练中取三种数据:

  1. 用户属性,包括年龄性别等基础属性,以及像持仓情况、投资次数这样的业务属性
  2. 每个UI样式包所打的UI标签数据
  3. 每个UI样式包在线上随机投放的曝光点击数据

经过特征工程处理,组成训练集去训练出DNN模型,在线上部署使用,输入是 用户属性 + (UI样式包对应的)UI标签 列表,输出是每个UI包的点击概率排序。

3.工程链路打通

从样式配置,到数据组装,到召回排序,到最后前端渲染,对接各个平台串联整个链路,在各场景实现千人千面UI推荐能力。

落地效果

当前个性化UI在数金多个场景落地,不同场景有不同的提升效果,按流量平均下来CTR(曝光点击率)提升20%左右,同时接入的场景CVR(曝光转化率)也同步提升。

其中流量最大的是支付宝首页财富生活模块,组合了几百种样式进行匹配推荐,CTR的提升在首页大流量的加持下为业务带来很大价值。

CTR提升最大的是理财Tab蓄水版直通车里的黄金和黄金票卡片,一千多 种样式,CTR 和CVR可以提升50%左右。在这个版本里卡片在屏幕占比较大,不同元素的展示对用户点击的影响是非常大的,这也是最适合接入个性化UI的场景。

UI7

分析

有效性分析

为什么这套方案会有效提升CTR,尝试回答几个常见问题:

1.疲劳度

是不是用户对一成不变的样式有疲劳度,只要有新的样式点击率就会提高?我们在每个场景里上线个性化UI,会分几个桶进行AB效果对比,其中随机桶是对组合生成的几百个样式进行随机投放,比如这是财富生活的实验数据:UI5-2

可以看到随机投放组合出来的样式效果并不好,并没有因为样式多了就能提升,反而因为组合出来的某些质量相对差的样式影响CTR下降,这在多个场景里都有类似的的情况,可以看到CTR的提升并不是疲劳度新鲜UI带来的。

2.UI外的因素

是否是UI以外的因素影响CTR,例如不同的产品/权益内容?每个展位接入的AB实验里,产品和权益是固定不变的,个性化UI用不同的表述方式(图片/文案/样式/排版)展示同样的产品和权益在同一时间随机人群进行AB对比,没有其他因素干扰。

3.新样式抢眼

是否有几个很吸引眼球的UI拉高了CTR,没什么人群偏好差别?很有可能因为原来的样式不抢眼,我们组合出来的某些样式加红加粗天然吸引用户注意力,实际上我们实践中因为设计规范的约束不会做太抢注意力的设计,同时我们也分析了,不同样式在随机投放和算法投放的差别,例如下图玩转理财场景里,一些样式算法推荐的人群比这个样式随机投放它的点击率提升了几十个百分点,说明不同人对这些样式有明显偏好,算法把这些样式分配给真正偏好的人,带来了点击率提升的收益。UI6

归因分析

我们看到了用户对不同UI表述展示方式有不同偏好,期望能沉淀出具体人群跟UI的偏好关系,给设计师和业务方在新场景设计中提供参考帮助。得益于UI语义标签体系,我们是可以沉淀出UI偏好和画像的。

我们探索尝试了多种方法去做归因分析,包括:

  1. 数据统计,直接统计线上不同人群在不同样式的点击率,在图表上找出点击率的差异,沉淀偏好结论。
  2. EBM可解释性模型,解释DNN模型中各特征对最终效果的贡献度,以及交叉特征观察人群特征和UI标签的关联关系。
  3. 网格分析,在表格上将多个人群特征-UI标签进行组合,列出所有组合的点击率,从中找出点击率变化规律和差异,找出偏好差异。
  4. 标准化偏好,CTR(UI特征,人群特征,对应产品)/CTR(人群特征,对应产品),衡量单个UI元素在指定人群特征里对点击率的影响程度,再进行对比找出偏好差异。
  5. 单样本方法,建模的方式分析人群特征和UI特征的贡献度。

初步可以沉淀出一些偏好洞察,例如:

  1. 理财货架场景上,收入较低者偏好产品名称,普通/高收入者偏好产品推荐语。
  2. 按钮颜色上,总体红色比蓝色更吸引,但越年轻的用户,越偏好支付宝蓝。
  3. 白领/初级投资者偏好金融元素,蓝领/高级投资者偏好品牌元素。
  4. 有信用卡者对走势图不敏感,无信用卡者更偏好走势图。
  5. 中年(40-50岁)年龄段特别偏好紧迫心理类型的标题,尤其偏好行业风口类。
  6. 理财货架中,整体上宫格样式效果远低于列表样式(宫格面积相对小),但年长者对宫格偏好不低。

归因洞察还未形成稳定分析体系,处于探索期,最终是期望探索到一种或多种分析有效通用的方法,沉淀为平台自动分析能力,能在平台沉淀 业务维度、场景维度、人群维度 的UI偏好画像。

产品化

在方案落地出效果后,我们看到这套能力是具备一定通用性,各业务可以接入获益的,于是我们致力于将它的能力产品化,沉淀多样式生成配置、通用高效的UI偏好模型等能力,让各场景可以快速接入。主要围绕接入效率和接入效果进行优化。

效率优化

个性化UI的接入成本比较高,需要设计素材,人工打标,前端/业务后台/中台对接工程链路,训练UI偏好模型,跟进实验,手工清洗数据分析。涉及流程长,对接的角色多,投入较大。在平台级大流量展位上接入这套能力,能产出很大的业务效果,这样的投入还能接受,但要覆盖更多的长尾场景,这样的接入效率性价比相对低,难以被接受,我们从几个方面建设去提升各场景的接入效率:

  1. 一站式配置平台(万花筒平台),完善样式配置、组合预览、打标、筛选审核、业务干预、样式三板斧发布、数据分析能力。
  2. 通用链路,提供从样式包召回、推荐、数据组装的通用工程链路,通过 BFF SDK 输出,普通场景只需要前端BFF接入SDK即可完成接入,减少与业务后台和中台的链路对接。
  3. 通用模型,用户特征和UI标签特征在各个场景里是通用的,也就可以训练出一个通用模型,映射用户特征和UI标签特征的偏好关系进行推荐,虽然效果相对专门训练的模型会打折扣,但适合长尾场景快速接入。
  4. 专用链路,数金多数场景与各业务平台和中台结合较紧密,我们把个性化UI能力嵌入这些平台,可以提供更好的结合业务和特定场景的一站式使用体验,提升效率,例如对接UCDP/梵高/毕加索建设banner链路、对接洛可建设微贷专有链路等。

以上几点是短期内会建设完成的能力,此外有两个正在探索中的提升接入效率的课题:

  1. 素材生成,当前接入的大多数场景需要设计师和运营一起产出多个图片和文案素材,这是接入流程中最大的成本,我们在探索素材自动生成的方案,沉淀符合设计规范的原子图片素材库,提供对不同场景尺寸和组合的处理适配能力,让图片素材可以在多个场景里复用。文案方面在中台智能文案基础上加入更多的业务语料,自动生产的文案更符合业务调性和用户偏好。
  2. 自动化打标,当前需要手工对每个元素进行打标,当标签趋于稳定,并且打标量达到一定程度后,可以通过算法理解图片和文本,归类到指定的标签进行自动化打标,减少人工投入。

效果优化

如何对样式推荐的效果进行进一步提升,也是我们持续在探索的,算法效果的提升,很大一部分取决于特征数据的丰富和准确,围绕数据会在这几方面进行尝试:

  1. 端智能结合,云端不直接推荐UI样式结果,而是推荐几个样式候选集,端上再进行实时重排。为什么端上重排可能会有效,端上可以根据用户的实时行为,判断用户看了还是没看(停留了、快速划过),再结合它的行为路径(点了有某个UI标签的展位、快速离开、去了很多二级页等),去综合判断是否因为疲劳度、兴趣变化等去切换其他样式,更好打中用户。
  2. 自动特征工程,训练专用的UI推荐模型时,会根据对场景的理解进行特征筛选和交叉组合处理,这里选择什么特征交叉由个人经验决定,我们借助AI中台的能力,尝试autoCross自动筛选有效的交叉特征,理论上能得到比手工交叉筛选更好的效果,同时减少特征工程的人员投入。
  3. 多模态建模,抽取图片素材/文本素材的高维特征,融合后作为UI标签的补充加入训练,丰富模型对UI的理解。
  4. UI标签演进,我们一开始构建的表现层/内容层的标签体系,几个场景使用下来有不错的效果,但还没能做到很通用地描述UI,正在尝试以描述因子/驱动因子的方式组织标签,我们会尝试不断迭代标签的描述粒度和通用性。

未来

个性化UI在金融场景我们实现了从0到1的突破,后续从1到100我们还需要做很多工作,除了上面说的产品化效率和效果优化,还有三个方向持续探索:

  1. 通用性,当前的标签体系、模型、工程链路都是围绕数金业务进行建设,实际上这套能力在多产品服务分发的场景都能适用,比较适合支付宝的业务形态,后续可提升通用性,在支付宝分发海量产品服务的展位上进行尝试。
  2. UI偏好画像,接入的场景足够多,标签体系足够完善,归因的方法探索出通用有效的方案后,可以沉淀用户的UI偏好画像,作为体验端的数据资产,为后续算法模型、业务设计提供价值。
  3. 能力扩展,我们围绕单展位模块的千人千面建设个性化UI能力,在这以外,跟智能展示相关的,还会有其他空间可以挖掘,例如全屏的注意力管理,用智能化的手段,避免全屏范围内 动画/红点/抢眼颜色/弹屏 不断抢占用户吸引力,以合适的方式分配用户注意力,提供更好的体验和效果,是一个可以探索的方向。

总结

我们针对数金的业务特性,在对产品的个性化UI展示上提出了 元素打散组合-语义打标-算法推荐-归因分析 的方案,落地取得了不错的效果,当前在将能力进行产品化沉淀的过程中,逐渐完善核心的 多样式生产、精准UI偏好模型、UI偏好画像 能力,让各长尾场景快速接入取得规模化的业务结果。过程中会碰到许多困难,但核心的“不同人有不同UI偏好”已被验证,我们会沿着这个方向,联合设计、产品运营、算法、质量一起持续深耕和拓展个性化UI能力。

用户端智能的应用实践

2019-3-25 评论(7) 分类:技术文章

去年团队在用户端上进行了一些简单的智能应用探索实践,这里记录梳理下。

现在很多“智能”,是普通推荐算法借深度学习的风包装的,核心也就是决策树/随机森林/SVM这些90年代已经提出的算法,我们这里的实践也是这样。在用户端上,智能应用最广泛的目前两个点:个性化和多媒体识别,我们主要实践是在个性化上,从原来所有用户都用同一套或几套规则,换成根据推荐算法给每个用户制定符合他个性化特征的不同规则。

实际上简单的个性化推荐也可以认为是规则,只是这个规则很复杂,里面的if/else/权重/概率计算不是人工写的,而是算法算出来的,大多采用监督学习的方式,这种需求大体实现步骤是:

  1. 建模,把问题转化为三个点:输入/输出/算法。也就是挑选特征集X,确定输出目标值标签Y,挑选算法尝试找出Y=f(X)。
  2. 取数,选择一坨线上数据,清洗出需要的特征和对应的目标值。
  3. 训练,不断调整算法/参数/数据,找出用户特征->目标值的一个靠谱转化方式,输出一个模型。
  4. 应用,部署线上应用,实时预测,分析效果输入特征。

按这个套路,寻找了一些应用场景,做了一些尝试。

智能push

智能push想做的是在合适的时间给人推push,提高点击率。基于一个假想:每个用户都有不同时间段的活跃时间点,如果push不是统一固定时间下发,而是选择每个用户对应活跃的时间点下发,可以提高点击率。这里的push是促活类的,例如资讯的push。这里的问题可以转化为:怎样根据一个用户已有信息预测出他的活跃时间点。

  1. 建模
    • 输入,选取可能跟用户作息有关的年龄/收入/城市等基础信息,再加上用户过去7天的push点击数据作为特征值。
    • 输出,用户的活跃时间点,精确到小时,用户一天可能在多个时间点活跃,需要输出多个时间点。因为需求原因,push时间点限制在8-21点。
    • 算法,8-21点共14个小时,14小时->14个分类,多个时间点活跃->属于多个分类,是个多标签分类问题,我们用多个二分类的方式实现,同样用随机森林的算法,14个时间点每个点都单独做一次二分类,每个二分类的计算结果都是0-1之间的数,可以当成这个用户在这个时间点的活跃概率,最后合起来,得到每个时间点的概率。再根据指定阈值确定是否活跃时间点,例如:
      20190324164008
  2. 取数:
    • 对过去的push数据进行处理,根据8-21点每个时间是否有点击push,合成多条特征数据,取过去8天的数据,1-7天的数据作为特征,第8天的数据作为目标值标签。
    • 清洗用户基础特征,数据转换,拼接push特征。
  3. 训练:根据第二步的数据分14个二分类(14个时间点)训练,得到14个模型,每个时间点一个模型,最后合成一个。
  4. 应用:经过算法模型可以得到用户每个时间点的活跃概率,有多种使用策略,可以通过阈值得到几个活跃时间点,也可以直接选用概率最高的时间点,还可以在业务发送push时,往后选取概率最大的时间点发送,例如业务12点发送push,选12-21点之间概率最大的时间点,把push进队列延迟到该时间点发送。

同样是经过了多轮的数据分析、模型调整、特征优化、AB实验、Android分链路优化等,最终同一条push内容,使用智能push链路和普通链路直接推送相比,点击率稳定在提升25%左右。

本地处理

push的优化上当时我们想做更多,除了活跃时间段,实际上影响用户点击push的还有他当前手机的实时状态,例如是否在看视频/玩游戏/放在口袋里等状态,在这些情况下用户点击push概率一定很小,如果在收到push后显示前能实时检测到这些状态,就可以推迟到更合适的时间显示。为此我们在iOS上做了一些尝试:

  1. 需要在用户收到push后执行代码逻辑,再决定是否显示push,经调研 Notification Extension 没有取消展示push的能力,VoIP 需要特殊权限,只能用普通的静默push去做。
  2. 每条push都改成静默push,收到后唤起执行代码,经过各种判断后创建Local Notification,在指定时间展示push
  3. iOS 无法直接判断用户正在运行的程序和手机状态,只能通过一些侧面属性判断,包括横竖屏状态、耳机插入、内存情况、cpu情况、锁屏情况、网络状态这些信息,模糊猜测用户当前所处环境,给出一条运算公式计算用户当前是否适合展示push,公式可以动态下发,根据效果调整。
  4. 用户本地保存上面跑出来的每个时间点活跃概率数据,把push延迟到下一个较活跃的时间点显示。

Android 也实现了差不多的方案,不过iOSAndroid的方案都有缺陷,iOS方面用户手动kill掉的APP情况会收不到静默push,会影响push的到达率,Android方面没有可靠的延迟展示push手段,若延迟的时间点APP处于非活跃状态,会无法展示,同样也是会影响 push 到达率,这些缺陷导致最后本地处理的方案实现后没有很好地应用上。

其他

除了智能push,还做了一些其他智能的尝试,包括

  1. 智能异常检测:客户端的一些异常很多时候无法通过代码准确地检测到,例如图形引擎在一些安卓机器上的花屏,没有报错的白屏/黑屏,期望是有统一的手段检测到这些显示的异常。做法是在端上内置机器学习框架,云端训练模型,输入是截屏图片,输出是异常分类,我们挑了三种情况作为异常截屏样本:白屏/黑屏/花屏,做一个简单的图形分类模型,典型的深度学习cnn入门级项目,压缩模型下发到客户端运行,用户运行过程中根据一定的规则抽样进行截屏检测,有异常可能即上报。
  2. 股票OCR导入:自选股票从不同APP迁移时,需要逐个股票进行输入->搜索->添加自选,这里要做的是在应用A自选列表截屏,在应用B导入截屏,OCR识别出截屏上的股票代码。OCR是比较通用的识别能力,直接用现成模型,配合一些逻辑处理就能实现了。

还有像智能预加载/智能分页/交互预测/交易流失归因分析这些实践,但还没有做完整,就不描述了。

感想

  1. 当智能基础能力的基建(训练平台,模型部署,数据打标等)做好了,要把智能能力应用到业务上时,需要的更多是业务理解和数据分析能力,多数精力会耗费在清洗数据、模型调优上,这个能力跟工程能力有很大不同。
  2. 简单的“智能”需求,不需要理解具体算法实现也能做出来,只是理解了算法可能对优化有帮助。
  3. 机器学习算法繁多,与过往计算机通用经验复用度不高,要达到创造/改进算法的程度门槛太高。
  4. 用户端体验上的智能应用在多媒体、IoT、系统级优化上比较有空间,其他的并没有找到特别大的应用场景。
  5. 即使做了几个智能的应用,也没法说已经入门了这个领域,只是接触到一点皮毛,而且当时去补的各种知识点在一段时间没有接触使用后,也很容易就遗忘了。

闲聊 Flutter

2018-8-27 评论(5) 分类:技术文章

移动端开发从08年开始就有个大家前赴后继不断追求的目标:跨平台,15年时 nwind 有篇雄文,详细调研了跨平台各流派,其中最后的 Dart 栏可以看到现在 Flutter 的雏形。可以看出来,Flutter 是从精简浏览器的思路演化过来的,实际上 web Flutter 从底层看是一致的,web 是提供了一层平台无关的独立引擎,可以看成平台只提供了画布,所有的UI组件、框架、事件处理都是 web 引擎封装处理。其实这种虚拟机方式是跨平台的正道,在 GUI 跨平台的道路上,JAVA FLASH 都是这种方式,在 PC 时代都取得过成功,只不过移动端时代只有 web 这种开放标准能平衡各大公司利益,延续下来了。

原本 web 作为跨平台的解决方案很完美,FB最初也信心满满用 web 技术做主 APP,但到最后还是搞不定性能问题,被迫回归原生。为什么web性能不行,上面雄文也说了,历史代码兼容,CSS复杂,DOM接口粒度大等问题,自然有牛人们继续不断去尝试解决这些问题,面对历史包袱满满的 web 引擎,首先尝试的当然是不断删代码删功能,做个精简版的 web 引擎,完全抛弃兼容性,只保留最主要的功能,据 Eric Seidel 说删完后快了 20 倍,于是朝这个方向经过几年的努力逐渐演化出 Flutter。(国外大厂可以花三四年时间做一个引擎且还在Beta,怕不怕?)

Flutter 的推出为略为沉闷的移动端技术注入了一些活力,底子强,包装好,只要接入引擎就能获得跨平台+高性能的特性。不过 Flutter 还是有不少缺陷:

  1. 动态化,国外对跨平台有偏执,国内对动态化的偏执更高,高速发展高压环境,随时发版修改是基础能力,Flutter Release AOT 无法动态化,理论上可以用 JIT 模式做动态化,但目前 Release 上没有 JIT 模式,不确定是否有性能上的问题,国内大厂接入使用少了一个很重要的理由。
  2. 体积,编译后iOS双架构15M+Android单架构约7M,不算太大,对小APP可以接受,但在大厂大APP普遍严格控制体积的情况下,使用又多一个大障碍。
  3. 语言,从 web 演化过来的框架,为什么不使用 JS 而是使用 Dart?可能出于性能考虑,Dart AOT 模式,但使用 Dart 绝对是 Flutter 推广的一大劣势,学多一门新语言就多一层障碍,Java Android 开发的推动,JS nodeJS 的推动,换个语言就不一样了。
  4. 生态,Flutter 刚推出不久,组件功能的完善度和丰富程度自然不能跟发展了十几年的iOS/Android原生以及web相比,虽说生态都是慢慢建立,但这一个从语言到工具到组件都是几乎从零开始积累,无法借用强大的前端生态或其他生态,难度会高很多,堪忧。

不管怎样,Flutter是一个宝库,一个完整的比 webkit 简单得多的引擎,源码很值得挖掘学习,现阶段国内关注 Flutter 也是学习居多,直接使用 Flutter 目前吸引力还不够大,但国内可能有另一个利用 Flutter 的途径:小程序。Flutter web 引擎简化中来,使用的也是 CSS flexbox 布局,但抛弃历史包袱重定规则,不兼容 web 也不是 dom 那套玩意,需要上层业务根据新规则限制写法,而小程序就是这样的限制框架,可以参考 Flutter 构建小程序渲染引擎,相对于 web 渲染性能好,相对 RN 渲染,同渲染引擎坑少,无需维护两个平台框架。可行性待研究,算是一个有趣的课题。

移动 APP 网络优化概述

2018-1-9 评论(26) 分类:技术文章

一般开发一个 APP,会直接调用系统提供的网络请求接口去服务端请求数据,再针对返回的数据进行一些处理,或者使用AFNetworking/OKHttp这样的网络库,管理好请求线程和队列,再自动做一些数据解析,就结束了。

但对于一些大型 APP,还会想针对网络的一些问题进行进一步优化,包括:

  1. 速度:网络请求的速度怎样能进一步提升?
  2. 弱网:移动端网络环境随时变化,经常出现网络连接很不稳定可用性差的情况,怎样在这种情况下最大限度最快地成功请求?
  3. 安全:怎样防止被第三方窃听/篡改或冒充,防止运营商劫持,同时又不影响性能?

对基于浏览器的前端开发来说,网络这块能做的事情很少,但对于客户端 APP 来说,整个网络请求过程是自由控制的,可以做很多事情,很多大型 APP 都针对这三个问题做了很多网络层的优化,一些新的网络层协议像 HTTP2 / QUIC 也是在这些方面进行了不少优化,在这里边学习边整理,大致列举一下常见的做法。

速度

正常一条网络请求需要经过的流程是这样:

  1. DNS 解析,请求DNS服务器,获取域名对应的 IP 地址。
  2. 与服务端建立连接,包括 tcp 三次握手,安全协议同步流程。
  3. 连接建立完成,发送和接收数据,解码数据。

这里有明显的三个优化点:

  1. 直接使用 IP 地址,去除 DNS 解析步骤。
  2. 不要每次请求都重新建立连接,复用连接或一直使用同一条连接(长连接)。
  3. 压缩数据,减小传输的数据大小。

逐条来看能做什么。

1.DNS

DNS 完整的解析流程很长,会先从本地系统缓存取,若没有就到最近的 DNS 服务器取,若没有再到主域名服务器取,每一层都有缓存,但为了域名解析的实时性,每一层缓存都有过期时间,这种 DNS 解析机制有几个缺点:

  1. 缓存时间设置得长,域名更新不及时,设置得短,大量 DNS 解析请求影响请求速度。
  2. 域名劫持,容易被中间人攻击,或被运营商劫持,把域名解析到第三方 IP 地址,据统计劫持率会达到7%。
  3. DNS 解析过程不受控制,无法保证解析到最快的IP
  4. 一次请求只能解析一个域名。

为了解决这些问题,就有了 HTTPDNS,原理很简单,就是自己做域名解析的工作,通过 HTTP 请求后台去拿到域名对应的 IP 地址,直接解决上述所有问题:

  1. 域名解析与请求分离,所有请求都直接用IP地址,无需 DNS 解析,APP 定时请求 HTTPDNS 服务器更新IP地址即可。
  2. 通过签名等方式,保证 HTTPDNS 请求的安全,避免被劫持。
  3. DNS 解析由自己控制,可以确保根据用户所在地返回就近的 IP 地址,或根据客户端测速结果使用速度最快的 IP。
  4. 一次请求可以解析多个域名。

其余细节就不多说了,HTTPDNS 优点这么多,几乎成为中大型 APP 的标配。至此解决了第一个问题 — DNS 解析耗时的问题,顺便把一部分安全问题 — DNS 劫持也解决了。

2.连接

第二个问题,连接建立耗时的问题,这里主要的优化思路是复用连接,不用每次请求都重新建立连接,如何更有效率地复用连接,可以说是网络请求速度优化里最主要的点了,并且这里的优化仍在演进过程中,值得了解下。

keep-alive

HTTP 协议里有个 keep-alive,HTTP1.1默认开启,一定程度上缓解了每次请求都要进行TCP三次握手建立连接的耗时。原理是请求完成后不立即释放连接,而是放入连接池中,若这时有另一个请求要发出,请求的域名和端口是一样的,就直接拿出连接池中的连接进行发送和接收数据,少了建立连接的耗时。

实际上现在无论是客户端还是浏览器都默认开启了keep-alive,对同个域名不会再有每发一个请求就进行一次建连的情况,纯短连接已经不存在了。但有个问题,就是这个 keep-alive 的连接一次只能发送接收一个请求,在上一个请求处理完成之前,无法接受新的请求。若同时发起多个请求,就有两种情况:

  1. 若串行发送请求,可以一直复用一个连接,但速度很慢,每个请求都要等待上个请求完成再进行发送。
  2. 若并行发送这些请求,那么首次每个请求都要进行tcp三次握手建立新的连接,虽然第二次可以复用连接池里这堆连接,但若连接池里保持的连接过多,对服务端资源产生较大浪费,若限制了保持的连接数,并行请求里超出的连接仍每次要建连。

对这个问题,新一代协议 HTTP2 提出了多路复用去解决。

多路复用

HTTP2 的多路复用机制一样是复用连接,但它复用的这条连接支持同时处理多条请求,所有请求都可以并发在这条连接上进行,也就解决了上面说的并发请求需要建立多条连接带来的问题,网络上有张图可以较形象地表现这个过程:

duolufuyong

HTTP1.1的协议里,在一个连接里传送数据都是串行顺序传送的,必须等上一个请求全部处理完后,下一个请求才能进行处理,导致这些请求期间这条连接并不是满带宽传输的,即使是HTTP1.1的pipelining可以同时发送多个request,但response仍是按请求的顺序串行返回,只要其中一个请求的response稍微大一点或发生错误,就会阻塞住后面的请求。

HTTP2 这里的多路复用协议解决了这些问题,它把在连接里传输的数据都封装成一个个stream,每个stream都有标识,stream的发送和接收可以是乱序的,不依赖顺序,也就不会有阻塞的问题,接收端可以根据stream的标识去区分属于哪个请求,再进行数据拼接,得到最终数据。

解释下多路复用这个词,多路可以认为是多个连接,多个操作,复用就是字面上的意思,复用一条连接或一个线程。HTTP2这里是连接的多路复用,网络相关的还有一个I/O的多路复用(select/epoll),指通过事件驱动的方式让多个网络请求返回的数据在同一条线程里完成读写。

客户端来说,iOS9 以上 NSURLSession 原生支持 HTTP2,只要服务端也支持就可以直接使用,Android 的 okhttp3 以上也支持了 HTTP2,国内一些大型 APP 会自建网络层,支持 HTTP2 的多路复用,避免系统的限制以及根据自身业务需要增加一些特性,例如微信的开源网络库 mars,做到一条长连接处理微信上的大部分请求,多路复用的特性上基本跟 HTTP2 一致。

TCP队头阻塞

HTTP2 的多路复用看起来是完美的解决方案,但还有个问题,就是队头阻塞,这是受限于 TCP 协议,TCP 协议为了保证数据的可靠性,若传输过程中一个 TCP 包丢失,会等待这个包重传后,才会处理后续的包。HTTP2的多路复用让所有请求都在同一条连接进行,中间有一个包丢失,就会阻塞等待重传,所有请求也就被阻塞了。

对于这个问题不改变 TCP 协议就无法优化,但 TCP 协议依赖操作系统实现以及部分硬件的定制,改进缓慢,于是 GOOGLE 提出 QUIC 协议,相当于在 UDP 协议之上再定义一套可靠传输协议,解决 TCP 的一些缺陷,包括队头阻塞。具体解决原理网上资料较多,可以看看。

QUIC 处于起步阶段,少有客户端接入,QUIC 协议相对于 HTTP2 最大的优势是对TCP队头阻塞的解决,其他的像安全握手 0RTT / 证书压缩等优化 TLS1.3 已跟进,可以用于 HTTP2,并不是独有特性。TCP 队头阻塞在 HTTP2 上对性能的影响有多大,在速度上 QUIC 能带来多大提升待研究。

3.数据

第三个问题,传输数据大小的问题。数据对请求速度的影响分两方面,一是压缩率,二是解压序列化反序列化的速度。目前最流行的两种数据格式是 json 和 protobuf,json 是字符串,protobuf 是二进制,即使用各种压缩算法压缩后,protobuf 仍会比 json 小,数据量上 protobuf 有优势,序列化速度 protobuf 也有一些优势,这两者的对比就不细说了。

压缩算法多种多样,也在不断演进,最新出的 Brotli 和Z-standard实现了更高的压缩率,Z-standard 可以根据业务数据样本训练出适合的字典,进一步提高压缩率,目前压缩率表现最好的算法。

除了传输的 body 数据,每个请求 HTTP 协议头的数据也是不可忽视,HTTP2 里对 HTTP 协议头也进行了压缩,HTTP 头大多是重复数据,固定的字段如 method 可以用静态字典,不固定但多个请求重复的字段例如 cookie 用动态字典,可以达到非常高的压缩率,这里有详细介绍。

通过 HTTPDNS,连接多路复用,更好的数据压缩算法,可以把网络请求的速度优化到较不错的程度了,接下来再看看弱网和安全上可以做的事情。

弱网

手机无线网络环境不稳定,针对弱网的优化,微信有较多实践和分享,包括:

    1. 提升连接成功率
      复合连接,建立连接时,阶梯式并发连接,其中一条连通后其他连接都关闭。这个方案结合串行和并发的优势,提高弱网下的连接成功率,同时又不会增加服务器资源消耗:
      20180109120421
    2. 制定最合适的超时时间
      对总读写超时(从请求到响应的超时)、首包超时、包包超时(两个数据段之间的超时)时间制定不同的计算方案,加快对超时的判断,减少等待时间,尽早重试。这里的超时时间还可以根据网络状态动态设定。
    3. 调优TCP参数,使用TCP优化算法。
      对服务端的TCP协议参数进行调优,以及开启各种优化算法,使得适合业务特性和移动端网络环境,包括RTO初始值,混合慢启动,TLP,F-RTO等。

针对弱网的这些细致优化未成为标准,系统网络库没有内置,不过前两个客户端优化微信的开源网络库 mars 有实现,若有需要可以使用。

安全

标准协议 TLS 保证了网络传输的安全,前身是 SSL,不断在演进,目前最新是 TLS1.3。常见的 HTTPS 就是 HTTP 协议加上 TLS 安全协议。

安全协议概括性地说解决两个问题:1.保证安全 2. 降低加密成本

在保证安全上:

  1. 使用加密算法组合对传输数据加密,避免被窃听和篡改。
  2. 认证对方身份,避免被第三方冒充。
  3. 加密算法保持灵活可更新,防止定死算法被破解后无法更换,禁用已被破解的算法。

降低加密成本上:

  1. 用对称加密算法加密传输数据,解决非对称加密算法的性能低以及长度限制问题。
  2. 缓存安全协议握手后的密钥等数据,加快第二次建连的速度。
  3. 加快握手过程:2RTT-> 0RTT。加快握手的思路,就是原本客户端和服务端需要协商使用什么算法后才能加密发送数据,变成通过内置的公钥和默认的算法,在握手的同时就把数据发出去,也就是不需要等待握手就开始发送数据,达到0RTT。

这些点涉及的细节非常多,对 TLS 的介绍有一篇雄文,说得很详细,在此推荐。

目前基本主流都支持 TLS1.2,iOS 网络库默认使用 TLS1.2,Android4.4 以上支持 1.2。TLS1.3 iOS 还处于测试阶段,Android 未查到消息。对于普通 APP,只要正确配置证书,TLS1.2 已经能保证传输安全,只是在建连速度上会有所损耗,有一些大型 APP 像微信就自行实现了 TLS1.3 的部分协议,早一步全平台支持。

最后

网络优化这个话题非常庞大,本文只是在学习过程中从优化思路上列举了目前业界常见的优化点,还有很多细节很多更深入的优化没涉及到,网络层实践开发经验不足,若有错误欢迎指出。

小程序技术方案探讨

2017-9-19 评论(3) 分类:技术文章

微信小程序上线大半年,大部分技术原理也有文章介绍了,本文尝试从需求出发探讨微信小程序技术方案的来源,以及最近公测的支付宝小程序技术方案的考量。

微信小程序

微信小程序的需求是让第三方开发者可以接入,可以使用微信的提供的接口去开发应用嵌入在微信里。对于这个需求,最简单的实现方案是:让外部开发者开发纯H5应用,在微信的 H5 容器里打开,容器提供微信 native 接口,就行了。在有小程序之前,已经有很多这样的业务接入,像京东购物,钱包里的各种友商大众点评/滴滴出行等,都可以认为是一个“小程序”,内嵌在微信里,能调用微信 native 接口,是不是沿着这种模式下去,把相应的接口开放给第三方,再提供个入口就行了?

实际上这种简单的方案不能满足需求,在产品上微信小程序有另外两个很重要的需求:

  1. 管控。作为一个平台必须对接入的应用有管控能力,必须能尽量精确控制应用的内容和类型,毕竟若出现非法应用平台是要承担责任的,H5 的方式太过自由,开发者可以随时改变整个应用的内容,平台难以检测到这些改变,无法管控。另外H5开发质量参差不齐,平台也无法管控,这对于一向有洁癖的微信来说无法接受。
  2. 体验。作为一个“小程序”需要让体验接近原生,而上述像京东购物这些普通 H5 页面的体验不太行,包括启动速度/页面切换流畅度都有问题,跟原生体验没法比。

所有小程序的技术方案都是为了这两个需求服务。

(更多…)

移动 H5 首屏秒开优化方案探讨

2017-8-14 评论(18) 分类:技术文章 Tags:

随着移动设备性能不断增强,web 页面的性能体验逐渐变得可以接受,又因为 web 开发模式的诸多好处(跨平台,动态更新,减体积,无限扩展),APP 客户端里出现越来越多内嵌 web 页面(为了配上当前流行的说法,以下把所有网页都称为 H5 页面,虽然可能跟 H5 没关系),很多 APP 把一些功能模块改成用 H5 实现。

虽然说 H5 页面性能变好了,但如果没针对性地做一些优化,体验还是很糟糕的,主要两部分体验:

  1. 页面启动白屏时间:打开一个 H5 页面需要做一系列处理,会有一段白屏时间,体验糟糕。
  2. 响应流畅度:由于 webkit 的渲染机制,单线程,历史包袱等原因,页面刷新/交互的性能体验不如原生。

本文先不讨论第二点,只讨论第一点,怎样减少白屏时间。对 APP 里的一些使用 H5 实现的功能模块,怎样加快它们的启动速度,让它们启动的体验接近原生。

(更多…)

React Native 源码导读(零) – 创建/运行/调试

2017-7-31 评论(10) 分类:技术文章 Tags:

最近工作需要,重新看 React Native (以下简称RN) 源码,了解机制,寻找优化空间,过程中看能不能整理出一些东西。

RN 这个项目已经是庞然大物,打开 github 项目主页,根目录下文件和文件夹就多达五六十个,看起来一脸懵逼,不知道哪些是源码,在看源码之前先理理 RN 最终用到哪些代码,项目是怎样创建,怎样跑起来的。以下皆以 iOS 端为例。

流程

先看看标准 RN 项目创建和运行过程:

  1. RN 根据教程装完环境后,会有一个全局命令 react-native,执行 react-native init AwesomeProject 可以创建一个新 RN 项目。
  2. XCode 打开自动生成的项目,编译到模拟器或真机,一个 RN hello world 程序成功运行了。
  3. 在模拟器运行同时会在 chrome 打开一个页面,在页面里使用 developer tools 可以直接断点调试 RN 页面上的 JS 源码。

疑问

上述流程跑下来整个过程是个黑盒,对黑盒里的处理有一些疑问点:

  1. react-native init AwesomeProject 这个命令做了什么,是怎样创建 RN 模板项目的?
  2. 项目 JS 源码在哪里,如何跑起来的?
  3. 怎样做到可以在 chrome 调试 JS 源码?

接下来一条条看。
(更多…)

晋升评审的套路

2017-6-26 评论(5) 分类:技术文章

很多中大型互联网公司都会有晋升评审,也就是对技术/产品等职位划分成若干个等级,每个员工都有一个等级,若要晋升到下一级,需要由几个评委面试决定是否合格。这跟传统公司的考职称差不多,只不过传统公司是通过考试,互联网公司是通过面试。

为什么会有这种晋级评审?等级是公司内部对员工的一种评价和定位,等级的参照物是公司内的所有员工,假如一个公司比较小,老板每天跟所有员工一起工作,在老板识人能力又没有问题的前提下,老板就很清楚每个人的能力,直接对他们排等级,不需要什么评审,得出来的结果其实是更公正准确的,因为这是根据平时工作过程中获取的大量信息综合考虑得出的结果。但在中大型公司做不到,老板认识不了那么多人,没在一起工作,没法对每个人给出公正的评价。若把评判权都交给一起工作的组长总监又不妥,因为职级的参照系是全公司,而不是组内或部门内,很容易出现偏颇/标准不一的情况。

于是出现一个评审系统,由公司里一些有经验的人士去判断某个人能不能晋升到下一个等级,而这些人平时很可能没有跟他们一起工作,仅仅是通过大概一个小时的陈述和沟通去评判,这导致了这一个小时里陈述的方式和沟通的技巧变得很重要,同样一个人同样的工作,不同的 PPT 不同的陈述方式,结果会完全不一样。这里大致说下我所了解到的套路,套路并不是贬义词,只是能帮助更好地表现自己的能力,让评委得出更公正的评价。

要点

先列一下在准备晋级评审 PPT 时我认为重要的几个要点:目标,重点,思路,数据。

目标

在准备晋级 PPT 时,得先搞清楚目标,这种 PPT 目标很简单只有一个,就是告诉评委我很NB我完全可以晋升到下个等级。时刻问自己 PPT 表现出这个目标了没有。

重点

只有一个小时时间,不可能面面俱到表现出所有,讲的内容多了评委也不会记得,必须突出一两个重点和亮点,用2/3以上篇幅去深入讲,并且要让人听得懂。有人误解了以为晋升评审是述职,罗列过去一年做的工作,这个其实很为难评委,评委需要帮你在你罗列的众多工作中寻找能体现你技术水平的点,整体印象也大打折扣。

另外需要有一些亮点,如果你陈述的都是业界常规做法,其他人也是这样做的,评委会觉得没什么特别,是个人都会这么做,若有自己独特的创新点和亮点,就算是小的点,让评委眼前一亮也是很好的加分项。其实这是这个评审制度本身的缺陷和局限,因为评委每次评审的人数太多,如果大家能力都没有太突出,那决定谁更好的方式就是谁更特别留下更多的印象,这也就是亮点的意义。

思路

讲思路不要讲细节,特别是代码细节,除非是非常有技术含量的,作为亮点的细节。着重表现自己在解决问题过程中的思路,表达出自己在项目/问题涉及到的方方面面都有考虑到(全局观),有深入思考的能力,面对一个问题有能力抽象出关键点,有能力分解问题,若最终能总结出类似问题的统一解决思路(方法论)更好。

数据

要有数据证明做出的成绩,经过你NB的工作后,是性能提升百分之多少,还是工作效率提高多少,还是投诉率降低多少,还是有多少个产品都在用你的东西,业务的核心数据是怎样,都要有证据证明自己不是在吹牛,尽量使用量化的指标。

结构

这里列个常用的结构供参考:

我是谁:工作经历
我做出了什么成绩:在本公司做的事情,负责的业务,做出的成绩。
1-3个重点:

  • 碰到什么问题,业界是怎么解决的,我在这基础上做了些什么,相对业界的做法有什么优点,在这过程中碰到什么困难,怎么解决,最终达到什么效果(数据)。
  • 碰到什么需求,业务特点是什么,有哪些技术挑战(安全/架构/协作/性能/稳定性/历史包袱/响应能力/兼容性/开发效率/自动化等等),我是怎样设计的(方案完整性),有什么优点和创新点,最终达到什么效果(数据)。
  • 碰到什么问题,我按什么样的思路尝试过哪些方法,深入钻研到什么程度,虽然最终做法跟业界一样,但经过我深入研究证明了这已经是最佳做法。
  • 碰到什么问题,有哪些开源项目解决了这些问题,它们有什么缺陷,我重新造的轮子比他们NB在哪里,做到这么NB的难点是什么,我怎么做到的,怎么证明真的NB不是我在吹。

未来计划
谢谢

当然并不是说都要按这个结构写,若个人能有创新发挥用更适合自己的方式陈述自然更好。

交流

交流环节里,一般评委都是会根据 PPT 内容问问题。

最容易被挑战的就是数据,若 PPT 上列的数据不是很常规就会被问为什么,对所有数据都必须准备好被质疑。

评委对某个点感兴趣会追问细节,一些重要的技术细节可以以附录的形式附在PPT后面,问到时方便讲解。

PPT 上提到的点最好对相关技术都了解清楚,例如提到客户端网络层优化,最好把网络层底层相关知识都准备好,评委可能会追问下去以考察技术深度。

有些评委喜欢问一些固定问题,例如你觉得为什么你应该晋级,你觉得有哪些不足,你后续的技术规划是什么等等。

公司一般会有每个职级对应的能力表,有些评委喜欢对着能力表问相关问题。

评审也是个双向学习的过程,如果你能让评委觉得学到东西了效果会很好。

很多评委喜欢问他擅长的专业领域内的问题。显然这个环节有一定运气成分存在,遇到匹配度高的评委通过几率直线上升。

最后

这种一小时判断一个人的晋级评审肯定做不到非常公正,有一些运气成分,甚至有一些关系成分,但已经是相对较好的一种形式,而且评审的准备过程中还能让自己梳理总结一下过去做的事情,虽然准备过程会很痛苦,但也会很有收获。晋升的前提当然是要有足够的技术能力/影响力和视野,这里一些套路只供参考,希望能帮助有需要的人更好地表现自己的实力。

iOS App 签名的原理

2017-3-13 评论(43) 分类:技术文章

iOS 签名机制挺复杂,各种证书,Provisioning Profile,entitlements,CertificateSigningRequest,p12,AppID,概念一堆,也很容易出错,本文尝试从原理出发,一步步推出为什么会有这么多概念,希望能有助于理解 iOS App 签名的原理和流程。

目的

先来看看苹果的签名机制是为了做什么。在 iOS 出来之前,在主流操作系统(Mac/Windows/Linux)上开发和运行软件是不需要签名的,软件随便从哪里下载都能运行,导致平台对第三方软件难以控制,盗版流行。苹果希望解决这样的问题,在 iOS 平台对第三方 APP 有绝对的控制权,一定要保证每一个安装到 iOS 上的 APP 都是经过苹果官方允许的,怎样保证呢?就是通过签名机制。

非对称加密

通常我们说的签名就是数字签名,它是基于非对称加密算法实现的。对称加密是通过同一份密钥加密和解密数据,而非对称加密则有两份密钥,分别是公钥和私钥,用公钥加密的数据,要用私钥才能解密,用私钥加密的数据,要用公钥才能解密。

简单说一下常用的非对称加密算法 RSA 的数学原理,理解简单的数学原理,就可以理解非对称加密是怎么做到的,为什么会是安全的:

  1. 选两个质数 p 和 q,相乘得出一个大整数n,例如 p = 61,q = 53,n = pq = 3233
  2. 选 1-n 间的随便一个质数e,例如 e = 17
  3. 经过一系列数学公式,算出一个数字 d,满足:
    a.通过 n 和 e 这两个数据一组数据进行数学运算后,可以通过 n 和 d 去反解运算,反过来也可以。
    b.如果只知道 n 和 e,要推导出 d,需要知道 p 和 q,也就是要需要把 n 因数分解。

上述的 (n,e) 这两个数据在一起就是公钥,(n,d) 这两个数据就是私钥,满足用私钥加密,公钥解密,或反过来公钥加密,私钥解密,也满足在只暴露公钥 (只知道 n 和 e)的情况下,要推导出私钥 (n,d),需要把大整数 n 因数分解。目前因数分解只能靠暴力穷举,而 n 数字越大,越难以用穷举计算出因数 p 和 q,也就越安全,当 n 大到二进制 1024 位或 2048 位时,以目前技术要破解几乎不可能,所以非常安全。

若对数字 d 是怎样计算出来的感兴趣,可以详读这两篇文章:RSA 算法原理(一)(二)

数字签名

现在知道了有非对称加密这东西,那数字签名是怎么回事呢?

数字签名的作用是我对某一份数据打个标记,表示我认可了这份数据(签了个名),然后我发送给其他人,其他人可以知道这份数据是经过我认证的,数据没有被篡改过。

有了上述非对称加密算法,就可以实现这个需求:

(更多…)

iOS 开发技术栈与进阶

2017-2-28 评论(20) 分类:技术文章

最近有一些开发朋友问我应该怎样提升自己的能力,回想起来做了这么久 iOS 开发,我也有过那种“让我做一个功能实现个需求我会做,但接下来怎样提高我不知道。”的时期,这里尝试列一下 iOS 开发的相关技术,再说说在学习进阶上我的一些想法。

iOS 技术栈

这里按我的理解给 iOS 相关技术分个类,以工程实现的角度,分成了基础、需求、效率、质量四个类别。基础指程序开发和 iOS 开发的基础知识和技能,需求就是产品的需求,有了基础技能,实现了产品需求后,剩下的事情就都是为了提高项目质量和提升开发效率。

大致的思维导图

iOS技术栈 (更多…)