产品需求和性能问题 canvas
在IG中,feed是由图片,视频和文字组成的。对于每一个图片和视频,咱们须要展现对应的图片说明和5条最近的评论。因为用户一般经过图片说明来说书图片背后的故事,这些图片说明一般是大段复杂的文字,甚至可能包含连接和emoji表情。
渲 染这种复杂文本的主要问题在于它滚动时对性能的影响。在Android中,文本的渲染是很慢的。即便在一个像Nexus 5这样的新设备上,一段有十几行复杂文本的图片说明的初始绘制时间可能会达到50ms,而其文本的measure阶段就须要30ms。这些都发生在UI线 程,在滚动时会致使app跳帧。 缓存
Android有不少用于文字展现的控件,但实际上,他们都用text.Layout进行渲染。例如,TextView会将String转化为一个text.Layout对象,并经过canvas API将它绘制到屏幕上。 app
因为text.Layout须要在构造函数中测量文本的高度,所以它的建立效率不高。缓存text.Layout和复用text.Layout实例 能够节省这部分时间。Android的TextView控件并无提供设置TextLayout的方法,可是添加一个这样的方法并不困难: 编辑器
使用自定义的view来手动绘制text.Layout会提高其性能:TextView是一个包含大量特性的通用控件。若是咱们只须要在屏幕上渲染静态的,可点击的文本,事情就简单多了: 函数
咱们能够不用从SpannableStringBuilder转化到String。根据你的文本中是否包含连接,底层的TextView可能会复制一份你的字符串,这须要分配一些内存。 性能
咱们能够一直使用StaticLayout,这比DynamicLayout要稍微快一些。 测试
咱们能够避免使用TexView中其余的逻辑: 监听文本修改的逻辑,展现嵌入drawable的逻辑,绘制编辑器的逻辑以及弹出下拉列表的逻辑。 优化
经过使用TextLayoutView,咱们能够缓存和复用text.Layout,从而避免了每次调用TextView的setText(CharSequence c)方法时都要花费20ms来建立它。 ui
因为咱们肯定会在下载评论后展现他们,一个简单的改进是在下载它们后就准备好text.Layout的缓存。
spa
在能够设置text.Layout缓存后,咱们的到来常数级的测量(measure)和绑定(binding)时间。可是初次绘制的时间仍然很长。50ms的绘制时间可能会致使明显的卡顿。
这50ms中的大部分被用于测量文本高度以及产生文字符号。这些都是CPU操做。为了提高文本渲染速度,Android在ICS中引入了 TextLayoutCache用于缓存这些中间结果。TextLayoutCache是一个LRU缓存,缓存的key是文本。若是查询缓存时命中,文本 的绘制速度会有很大提高。
在咱们的测试中,这种缓存能够将绘制时间从30ms-50ms减小到2ms-6ms。
为了更好的提高绘制性能,咱们能够在绘制文本到屏幕前准备好这个缓存。咱们的思路是在一块屏幕外的canvas上虚拟的绘制这些文本。这样在咱们绘制文本到屏幕前,TextLayoutCache就已经在一个背景线程中被准备好了。
默认状况下,TextLayoutCache的大小为0.5M,这足以缓存十几张图片的评论。咱们决定在用户中止滑动时准备缓存,咱们向用户滑动的方向提早缓存5个图片的评论。在任什么时候候,咱们都至少在任何一个方向上缓存了5个图片的评论。
在应用了全部的这些优化后,掉帧状况减小了60%,而卡顿的状况减小了50%。