25

天猫精灵App首页信息流是怎样实现的

 3 years ago
source link: http://mp.weixin.qq.com/s?__biz=MzU2MTk0ODUxOQ%3D%3D&%3Bmid=2247483822&%3Bidx=1&%3Bsn=db949a28fa289f39feaa7a930d6f0d56
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

我们先来简单介绍下信息流的概念。

从布局形式上来看也被称为“瀑布流”,如图,卡片大小错落有致,且无需用户翻页,新的内容通过懒加载自动添加到页面底端。

FNbIryq.png!web

从数据格式上来看也被称为“feed流”,“feed”即一种信息单元格,可以理解为某一段文字、一张图片、一段音频、一段视频等。那么App上的信息流可以简单地归结为:无限加载的交互+个性化推荐的内容。

再简单介绍下信息流的发展。自瀑布流的鼻祖Pinterest上线之后,国内各种类Pinterest交互的应用也如雨后春笋般出现,远的如今日头条、美丽说、花瓣网,近的如手淘、闲鱼、飞猪等,让你刷刷刷、买买买欲罢不能。信息流在手淘首页、购物车、商品详情页等随处可见。

jqmeErB.png!web

回到我们天猫精灵App,老版本首页存在流量分发效率低下的问题,让人失去往下翻的欲望。随着内容商业化的进一步推进,以及语音购逐渐为大众所接受,各种三方内容和商品源源不断地接入,在诸多矛盾日趋尖锐的情况下,首页信息流呼之欲出。

尽管有现成的实现方案可以参考,因天猫精灵App和天猫精灵音箱是一对孪生兄弟,因此需要考虑 运营投放问题:我们有几十款音箱设备,不同设备的运营投放内容不同,很大一部分设备需要维持老卡片投放,因此我们既要实现信息流,也要兼容老卡片;

接下来介绍天猫精灵APP首页信息流从0到1的工程化落地。

EV7VJz7.png!web

结合上图我们来梳理下这次改动对整体框架产生影响的几个点:

      1 、滑动动效

      2 、下拉刷新位置调整(原下拉刷新统一在顶部)

      3 、老卡片兼容(个数随机)

      4 、信息流无限加载

      5 、信息流卡片渲染(需要独立的承载容器)

除了实现上述看得见的需求以外,还要解决看不见的问题:

1 如何维持内存水位?

2 、如何保障滑动流畅?

接下来的任务就是进行合理的技术选型和首页框架设计,清晰划分每个区块,然后在每个区块填充各自的元素和动效,管理各自的内存占用。

  • 渲染技术选型

App3.0 首页的框架设计已经可以支持卡片扩展、内存复用、滑动流畅等基本能力,目前可支持20多种卡片,但毕竟是native实现的,无法支持卡片样式的在线增加和动态更新。从长远来看,信息流不是简单的一种交互方式,而是各种业务的分发载体,一成不变的卡片样式势必无法满足源源不断的新业务。那么,卡片的 在线发布 动态更新 就显得不可或缺。

据了解,手淘使用了自研的DinamicX方案来实现整个首页所有卡片的渲染, 概括来说,DinamicX就是把原本在本地开发的布局文件放到了云端,因此,DinamicX卡片天然就是信息流的好搭档,既拥有native的体验,也支持在线发布和动态更新。

综上,卡片渲染的技术选型基本确定:为兼顾项目上线时间和卡片样式更新,老卡片复用原有的native方案,信息流卡片选用DinamicX方案。

  • 控件内存介绍

我们用梯子来类比App里面控件的内存占用情况,一种控件像消防梯,伸缩自如,但它的内存占用也是线性增长的,这种控件我们称之为 “线性控件”(LinearLayout) ,如下图,这类控件一般用于承载一屏以内的视图。

IZVZrqQ.png!web

线性控件

另一种控件就像自动扶梯,它的高度固定,但是它的踏板可以循环往复地滚动,因此占用的内存就是可见部分所占的内存,即 “内存复用” 技术(如下图),这类控件我们称之为 “循环滚动控件”(RecyclerView) ,常用于承载列表类卡片。

U7VRB3j.png!web

循环滚动控件

  • 火车模型

初看视觉稿,我们自然会想到把各个区块一个接一个地串连起来,就像一列火车跑在轨道上,第一节是顶部区块,第二节是动效执行区块,第三节是老卡片区块,第四节是信息流区块,除了第四节使用“循环滚动控件”,其他几节使用“线性控件”,我们把这个模型称为 “火车模型”

VrAJNnN.jpg!web

这种模型的优点是实现简单,基本没有什么衍生的副作用。如果不需要兼容老卡片,那么这个模型基本上就够用了,为什么这么说呢?上文我们介绍过背景,不同的设备投放的卡片不一样,我们就必须考虑极端情况下,运营同学配置了几十个甚至更多的老卡片,这时就存在内存溢出的风险了。

  • 自拍杆模型

于是我们思考另一种模型—— “自拍杆模型” ,自拍杆的特点是一截套一截,每一截都可以伸缩滑动。

Q3qyYz3.gif

如下图,我们用动画容器套下拉刷新组件,下拉刷新组件套一个循环滚动控件,这个循环滚动控件我们称之为 “父容器” (如红色虚线框),用于承载老卡片和一个循环滚动控件,里面这个循环滚动控件我们称之为 “子容器” (如绿色虚线框),用于承载信息流卡片。

rY3UBba.png!web

嵌套方案

上文提到,“循环滚动控件”占用的内存仅屏幕可见区域所占的内存,那么“自拍杆模型”中的“父容器”就解决了老卡片带来的内存溢出风险。但是, 有利必有弊 ,从“火车模型”转为“自拍杆模型”,牵一发而动全身,如果我们对“自拍杆模型”不做任何优化,就会发生信息流无法滑动或者只有信息流能够滑动的现象,这就是App开发中令人头疼的 “嵌套滑动冲突” 问题,它存在于每一层嵌套之中,就好比自拍杆从顶上往外拉,当你去拉第一截时,第一截没拉出来,反而把第二截或者第三截拉出来了,或者只有第一截能拉动,其他几截怎么都拉不动,这个场景想必大家可以脑补一下,体现在App上如下:

如果“嵌套滑动冲突”处理不好的话,更为严重的副作用是“父容器”和“子容器”将无法发挥内存复用的能力,首页内存会随着老卡片或信息流卡片的加载而暴增。

  • 解决嵌套滑动冲突

解决该问题的核心就在于对用户滑动事件的合理消费,以“父容器”和“子容器”之间的嵌套为例:

1 、随着父容器的滑动,当子容器出现在屏幕可见区域时,抢夺用户滑动事件。如图红色标注,子容器在滑动过程中,将用户手指滑动的距离 x 通知给父容器,然后由父容器根据自身当前情况决定是否消费滑动距离,如果父容器消费了滑动距离 y ,那么子容器就会消费剩余的滑动距离 (x-y) ,惯性滑动事件也是如此,流程如下左图。

2 、当子容器滑动到临界位置时就释放滑动事件交由父容器处理。结合自拍杆脑补,当往外拉时,里面的一截先往外拉,当它拉到底时套着它的那一截再开始往外拉;当往里缩时,套着它的那一截先往里缩,当这一截缩到底时里面的一截再开始往里缩,App上效果如下右图(示意图来自网络)。

Nzeueqq.png!web

嵌套滑动时序图

Q3qyueJ.gif

嵌套滑动 demo

解决思路理清楚了,那么任何一层的嵌套滑动问题都随之迎刃而解,剩下的细节填充只是时间问题了,最终的实现效果如下视频:

  • 首页性能

首先来看下首页连续滑动时的内存水位:

miIRni6.png!web

可以看出,首页内存占用基本维持在390M上下,远低于OOM 崩溃安全线,再来看下信息流加载过程中的滑动流畅性:

视频中的柱状图中每一根的高度即代表每一帧的渲染耗时,滑动优化的细节在此不做展开,总体来看,天猫精灵App首页滑动性能不低于业界头部App

  • 总结

本篇主要介绍了天猫精灵App首页框架设计,为兼顾不同设备的运营投放,因此选择了“自拍杆模型”,并解决了该模型引起的“嵌套滑动冲突”问题,首页的整体内存水位因此也得到了有效控制。

(全文完)

其它精选文章:

扫码或长按关注微信公众号: 天猫精灵技术。

ENfAjab.jpg!web讲述天猫精灵背后的技术


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK