加载等待的艺术
读者肯定见过很多等待程序内容加载的场景,大部分是类似图3-1所示的加载动画。
图3-1
不得不说,加载动画是技术妥协的产物。加载动画所代表的异步加载技术,实际上是因为大家发现很多情况下同步加载执行速度太慢,可能会卡住整个界面,才被用来“曲线救国”的。
为什么有些同步加载执行慢呢?这就要从当代计算机的架构说起。
现代计算机执行任务主要依靠CPU的运算和存储设备对数据的读写。有些任务需要占用很多的CPU,比如图片解码、MATLAB里一些数学方程的运算,以及一些比较复杂的网页的排版(例如,在手机上打开www.qq.com,页面元素太多,排版非常耗时)。这种任务叫作CPU密集型任务,一般不适合用同步加载的方式执行。
也有些高度依赖存储设备的任务,比如读取数据库里的一些信息、加载磁盘上的一张图片,以及从网上下载一部电影。不同的存储设备的运行速度有天壤之别,一般来说,模块离 CPU 越近,运行速度就越快,大体的速度快慢排序是寄存器→高速缓存→内存→硬盘→网络。可以把整个互联网看成计算机的一个大硬盘,只不过它是用网线来传输数据的。内存是一个分水岭,排在内存前面的存储设备的运行速度很快,可以做到“立刻加载”。硬盘因为是机械结构,所以运行速度非常慢(大概是内存的十万分之一)。凡是跟内存之后的存储设备打交道的任务,都不适合同步加载,这样的任务叫作I/O密集型任务。
所以加载动画的使用方法是:当用户跳转到一个新的场景,或者加载一个新的网页时,必须要执行一些CPU或I/O密集型的耗时任务才能让用户看到内容,这时前台让用户先看到加载动画,后台开启新线程执行这些任务。
经常有产品经理说,用户永远是希望马上看到内容的,所以带加载动画的交互方式不好。当然,没有什么技术是不可能实现的,在有些场景下也可以不使用加载动画,秘密就在进场动画上。
我们在微信中打开一个新的聊天窗口时,会看到窗口中从屏幕右边滑过来的动画,这就是一个入场动画。入场动画的动画时间一般是200~300ms,在人眼看来是一瞬间的事情,但对CPU来说非常漫长。前台通过做入场动画代替传统的加载动画,后台开新线程做耗时任务,等动画做完,入场完毕,内容也加载得差不多了。
这里读者可能会问,一边做入场动画,一边做耗时任务,动画不会卡住吗?一般来说不会。动画实际上是一遍一遍地渲染界面(间隔16ms左右,所以看起来是连续的)。界面的渲染要经过测量、排版、绘制三个阶段,其中前两个阶段是依赖CPU完成的,绘制只有小部分靠CPU,大部分是靠GPU。还有一点,不管是Android系统还是iOS系统,动画一旦启动,就不再测量和排版了,只是每一帧绘制一下,刷新一遍屏幕就可以了,这时GPU在满负荷运作。
做动画是一个GPU密集型任务,无论后台执行的是CPU密集型任务还是I/O密集型任务,都不用担心会卡住动画。但是有一点需要注意:如果内容加载只花了100毫秒,出场动画做到一半的时候就把该干的事情干完了,这时直接把内容展示出来,动画多半是要卡住的,因为新内容渲染到屏幕上一般是要经过重新测量和排版的。在一个非常流畅的动画中间卡了哪怕一帧,用户都能明显感觉到。所以一般的做法是在动画结束后再填充加载好的结果,一下子展示出来。
加载动画是一种简单又常见的交互,其根本目的是使界面交互更流畅自然。