内容简介:各大手机厂商发布的旗舰机,都将 90Hz 甚至 120Hz 高帧率流速屏当成了标配,那这对我们实际开发的 App 会不会有影响?原本在 60Hz 下,每帧只需保证 16ms 内绘制完成就可以做到流畅,换到高帧屏中,实际留给我们绘制时间是缩短了的,是否对我们的代码质量要求更高?推荐一篇文章,来讲讲对此的影响,希望对大家有帮助。
各大手机厂商发布的旗舰机,都将 90Hz 甚至 120Hz 高帧率流速屏当成了标配,那这对我们实际开发的 App 会不会有影响?原本在 60Hz 下,每帧只需保证 16ms 内绘制完成就可以做到流畅,换到高帧屏中,实际留给我们绘制时间是缩短了的,是否对我们的代码质量要求更高?
推荐一篇文章,来讲讲对此的影响,希望对大家有帮助。
以下文内的 "我" 为文章原作者。
前言
昨天在 IT 之家留言说,如果应用无法满足 120hz 的绘制会怎样?假设如果绘制一帧的时间,如果大于 1/120 秒,哪怕是多了 1 毫秒,就会导致应用在 120Hz 的手机上也就变成了 60Hz。
后来仔细想想,这句话说的并不是特别严谨,为什么这么说呢?
一、证明我的观点
首先我写一个 demo 来证明我的观点。
1.1 满帧的应用
public class MyTextView extends TextView {
int i = 0;@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (i < 6) {
this.post(new Runnable() {
@Override
public void run() {
MyTextView.this.setText(i++ + "");
}
});
}
}
}
我写了一个只绘制 6 帧的一个界面,Activity 中包含这个 MyTextView,通过抓 Trace 可以看到,下面这个 6 帧绘制图,都是在一个 Vsync 周期( 图中黑白相见的色块,均是一个 Vsync 周期 )绘制一帧。
为什么第一帧会有点延迟,主要是因为启动时,主线程在干了些其他事情,好在绘制花不了太多时间。
大概绘制也就 3 毫秒左右,毕竟界面简单。
1.2 帧数减半的应用
public class MyTextView extends TextView { int i = 0; @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); try { Thread.sleep(16); } catch (Exception e) { } if (i < 6) { this.post(new Runnable() { @Override public void run() { MyTextView.this.setText(i++ + ""); } }); } } }
我手动人为在 MyTextView.onDraw()
方法中 sleep 了 16 毫秒,来模拟很多应用实现的不好,导致绘制耗时的情况,例如 View 层级厉害复杂,甚至在 onDraw()
方法中进行文件读写。
通过抓 trace 可以看到,原本之前的例子( 1.1 )中 6 帧由 6 个时间周期绘制完成,现在变成了 6 帧由 12 个时间周期完成了。
1.3 小结
这就是我一直想说的一个观点, 120Hz 的手机体验不一定有 90Hz 的手机好的原因,一旦主线程一次 traversal(一次 ViewRootImpl 的 onDraw() 的遍历)的时间超出了 vsync 的时间周期 1/120s,就会导致帧率直接砍半,降为 60 帧 ,90Hz 可以让这个折半降帧率的现象减少。
二、绘制一帧指的是什么
为什么我说我的观点不准确?其实是我太笼统的定义了绘制一帧这个词。
在硬件加速开启的情况下:
-
如果我将绘制一帧的时间定义成: 主线程一次 traversal ,那我说的话是正确的。
-
如果我将绘制一帧的时间定义成: 主线程一次 traversal + renderthread 一次渲染 ,那我说的话是不严谨的。
2.1 引发我思考的 trace
因为要让主线程一次 traversal + renderthread 超时不是特别好写 demo,我就用一个昨天引发我思考的一个 trace 给大家讲解一下。
大家从第一个黄色 F 的圆圈开始看。
UI Thread 的 doFrame + RenderThread 的 DrawFrame 的时间超出了一个 Vsync 周期,接下来每一帧的情况,都是按照第一帧绘制的情况一样的情况运行,最好造成的现象就是虽然帧数是满帧,但是每一帧其实都是延迟显示的。
用一句哲学的话来概括就是: 你眼睛看到任何事物,其实都是事物过去的样子 。
假如主线程一次 traversal + renderthread 一次渲染时间超出了 vsync 周期不多,这样子的应用大概率是可以满帧运行( 满帧不代表显示正常 )。
我上面一句话中用了不多,大概率这种模糊的文字,因为这个情况下能否满帧的临界点比较难定义。
2.2 如何定义这个 120 变成 60 帧的临界点
继续下面这个图,你会发现 UI Thread 的 doFrame 会非常 happy 的按照 Vsync 信号执行,但是下面 renderthread 已经忙的不可开交了。
其实第一帧已经延迟了,假如运气不好导致了 drawframe 的时间横跨了 2 个 Vsync 周期,还是会导致丢帧,这个时候就从 120 帧降为 60 帧。
三、总结
3.1 理想中的结论
假设开启硬件加速,一帧绘制的时间 = UI 线程( T1 )+ RenderThread 线程( T2 )。
那么 Vsync 时间的周期是 1/120s,约等于 8.3ms,此时:
-
如果 16.6ms > T1 > 8.3ms,会导致从 120 帧降为 60 帧。
-
如果 T1 <8.3ms,16.6ms> T1 + T2 > 8.3ms,虽然还是满帧,但是你看到的永远是前一帧的画面。
-
如果 T1 + T2 > 16.6ms,毫无疑问会导致从 120 帧降为 60 帧。
上面理论看起来是很完美的结论,千万别把这个当做定理去记忆,因为现实会比这些情况复杂的多。
3.2 显示中的结论
可能会有人说现在 CPU,GPU 那么强,怎么可能会超时呢?
举个微信在 865 的手机上,滑动消息列表的例子,T1 约等于 6ms,T1 + T2 约等于 8.2ms。
这还并不是太过复杂的界面,大家会发现,6ms 已经很接近 8.3ms 了。
3.3 给开发者的建议
对于要适配 120hz 手机的应用的工程师,你们要注意以下事情。
-
避免 T1 > 8.3ms,也就是要减少主线程干的活,要减少一次 draw 的时间。千万别在 draw 的流程进行文件访问或者 sleep。
-
避免在滑动响应的时候 T1+T2 > 8.3ms,虽然界面在显示的时候,推迟一帧用户是察觉不了的,但是在滑动的响应中如果推迟一帧,可能会影响 "跟手" 的体验。
-
千万别关闭硬件加速,一旦关闭硬件加速 T1+T2 > 8.3ms 就会导致 120 降为 60 帧。
四、尾巴
说到这里,如果听得懂我在说什么的人,应该能懂我想表达的意思。
如果不懂我说的什么的人,希望你去补一下以下知识点,再回过头来看这个文章。
知识点 1:
TextView.setText 到屏幕显示文字,整个过程发生了什么。
知识点 2:
硬件加速之后,主线程( UI Thread )和 RenderThread 之间的协同工作的方式。
知识点 3:
APP 界面和 SurfaceFlinger 之间的关系
可能会有人说,你揪那么细致干嘛,对于用户来说,掉了几帧的影响也不大,,但是对于性能优化工程师来说,有时候就是要纠结那几个丢帧的情况,只有做好了每一帧的完美绘制,才能给用户带来最完美的用户体验。
- End -
文章看完了,最后我换个角度补充一些观点。
手机硬件虽然增加了高帧屏,确实看似对每一帧绘制的时间要求更短了,但别忘了,在升级屏幕的同时,对其他硬件( CPU、GPU、ROM等 )也做了相应的升级。虽然留给我们的刷新时间缩短了,但计算和渲染绘制的能力,也相应的提高了。
那么到具体的代码上,你在旧手机上绘制一帧需要 10ms 的代码,在 120Hz 的旗舰机上可能只需要 6ms,两相抵消之下问题不会太大。
所以最后核心还是在日常编码的过程中,随时注意性能的问题。管他高帧低帧,领导觉得" 不 跟手 ",咱就嘚优化。
本文对你有帮助吗? 留言、转发、点好看 是最大的支持,谢谢!
公众号后台回复成长『 成长 』,将会得到我准备的学习资料。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Android 旗舰机标配的高帧屏 (120Hz),对各位 App 开发者有什么影响?
- Guns 旗舰版2.1发布,更新树形表格
- LaravelWeb v2.1.1 旗舰版发布,升级文件上传组件
- Guns 旗舰版 2.2 发布,更简洁的管理系统
- Guns 旗舰版 2.4 发布,更简洁的管理系统
- Guns旗舰版 2.5更新,更简洁的管理系统
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
多任务下的数据结构与算法
周伟明 / 华中科技 / 2006-4 / 58.00元
本书和传统同类书籍的区别是除了介绍基本的数据结构容器如栈、队列、链表、树、二叉树、红黑树、AVL树和图之外,引进了多任务;还介绍了将任意数据结构容器变成支持多任务的方法;另外,还增加了复合数据结构和动态数据结构等新内容的介绍。在复合数据结构中不仅介绍了哈希链表、哈希红黑树、哈希AVL树等容器,还介绍了复合数据结构的通用设计方法;在动态数据结构中主要介绍了动态环形队列、动态等尺寸内存管理算法。在内存......一起来看看 《多任务下的数据结构与算法》 这本书的介绍吧!