在知乎回答了一个“为什么微博的app在iPhone比Android上流畅”的问题。后面部分是一个典型的动画卡顿的性能分析过程,因此帖在这里。有编程问题可以在这里交流。知乎链接

=========================================================

我来说下我所知道的事情。我不知道iOS为什么流畅,但我知道一些Android为什么不流畅的原因。

首先,就题主所说的问题,我用iPad和小米Pad对比了一下微博滑动滚屏这件事情(2014年8月10日目前微博app最新版本)。正如题主所说,直观感受上明显感觉iOS要流畅、舒服。

在这件事情上我认为主要是这三个原因:

  1. 速度曲线。
    当你滑动界面然后松手,这时界面会继续滑动,然后速度减小,直到速度为0时停止。iOS下速度减小的这个过程比较慢,尤其是快要停的时候是慢慢停的,视觉上有种很顺滑的感觉;Android下则从松手到停要快很多,相比之下有种戛然而止的感觉。
    从数据/技术角度来看这个事情,我们滑动界面的最终目的不是为了“动”,而是为了“停”,因此只要平滑的到达目的地,似乎越快完成这个过程越好,所以Android的选择是理所当然的。但事实是,大家普遍更喜欢iOS的方式,这样做显得更顺滑、更优雅。
  2. 帧率。
    绝大部分时间两者都能保持60FPS左右的满帧率。但都会有偶尔的掉帧。并且Android上要比iOS上严重很多。(好吧,比起前两年,已经好太多了。)我前前后后滑动了几十次,iOS在前面遇到1次掉帧,后面就很稳定了。而Android几乎每滑动一次都会伴随一次掉帧。这完全就是真真实实的卡顿,用户必然会感觉到那一刻的不流畅。Android掉帧的原因我后面再详细分析。
  3. 触摸响应速度。
    从手指碰到触摸屏,到屏幕上显示处理这次触摸产生的画面,是需要时间的。时间越短感觉越跟手。据说iOS的触摸屏的处理时间要比一般的Android手机快,这不是我的专长,不知道怎么验证。但在软件系统层面,Android的显示机制是app-->SurfaceFlinger-->Display,这比传统的app-->Display多了一步,主要基于这个原因,画面最终输出到屏幕要比传统的方式慢一帧(16.7ms)。

我觉得第1点造成的影响最大,恰恰却是最技术/设备无关的。如果微博app或者Android系统要改变,很容易就可以调得跟iOS一模一样。但正是由于这是产品形态上的差别而不是纯粹技术上的优劣,反倒成了Android最不太可能改变的。

第2点的影响其次,当然是指在目前这个大部分时候都能满帧的情况下。这方面是Android从早期到现在进步最明显的方面,使用了很多方法来优化帧率。但就算现在Android进化了很多,硬件性能也进化了很多,却仍旧不可能彻底消灭掉帧的情况。

第3点通俗的讲就是跟手性,跟手性的重要性不言而喻,但现在的差别比较细微,且具体数据我也不清楚。
我想过一个办法让桌面、微博这种内容和手一起动的应用绘制到屏幕的速度快一帧(16.7ms),其实就是抵消之前提到的慢的那一帧,需要framework层和app层一起配合改动,目前已申请了专利但代码还没进,将来有时间了应该会进到MIUI。感兴趣的可以看看专利:滑动操作响应方法、装置及终端设备

最后我来用专业技术分析一下微博app在Android里掉帧的原因。非编程人员可以不看下去了。(这个过程我使用的是小米3高通版+最新版微博app。)

最初,我认为这种现象很像GC(垃圾回收)导致的,于是打开logcat观察每次卡顿的时候有没有GC发生。结果发现并没有。停下来的时候才会有GC,这说明微博app在滑动过程中控制得不错,在停下来的一刻才去分配内存,使GC不影响帧率。

然后我打开“开发者选项”->“GPU呈现模式分析”->“在屏幕上显示为条形图”(好像是4.4才有这个选项,之前的只能在dumpsys里看),这会在屏幕上直观的显示每帧绘制花费的时间。屏幕上有条基准线大概是16ms,如果超过这条线则很有可能掉帧了。如果下面的蓝色部分很长则说明是软件draw的部分太费时,那么可以通过traceview来继续分析draw的java代码。(我假定了现在的app都是硬件加速的方式。)如果中间红色部分很长则说明是OpenGL ES绘制过程太费时。可以用gltrace来分析OpenGL ES的调用过程。
打开选项后使用微博app,发现虽然偶尔有超基准线,但并不严重,并且卡顿的时候并没有明显异常。

于是我开始使用systrace,这下就很明显了:

可以发现每隔几帧,就会有一次大间隙,图中我圈了红圈圈的两个地方都明显超过正常的耗时时间。现在问题是,这些地方的代码在干什么呢?是什么花费了这么长时间?

放大后发现都是这两个:obtainView和setupListItem。它们都是在draw之前调用的,这也解释了通过上一步的方法(“GPU呈现模式分析”)为什么没有显示出来,因为那个方法只展示draw里面的时间消耗。
通过在源码里搜索obtainView和setupListItem,可以发现是AbsListView的obtainView方法和ListView的setupChild方法打下的这个log。

接下来,我们用traceview来看看这两个方法里面究竟调用了什么代码导致耗时过长。
跟踪obtainView最终到了这里:

代码混淆了,看不太出来了,也懒得再跟下去了。但随便点了点然后看到:

TextView.setText用了挺多的时间。感觉Android团队应该优化优化这个方法。
然后再来跟踪setupChild方法:

都是measure占用的时间,并且调用次数比较多。这应该说明了View的数量不少。用hierarchyviewer看了下,的确不少,一条微博有50多个View:

结论:我们最终大致找到了耗时的代码及部分原因。这两个耗时方法应该都是有可能减下来的,但应该不那么容易。
在Android上实现功能比较容易,但如果默认实现有了性能问题,要想解决就不好说了,有时候要绕很远。

最后,列一些相关链接:
Android图形架构(绘图慢一帧的事情这里有说):http://source.android.com/devices/graphics/architecture.html
Android Performance Case Study:http://www.curious-creature.org/docs/android-performance-case-study-1.html

Android中app卡顿原因分析示例的更多相关文章

  1. android中app卡顿优化问题

     所谓app卡顿原因就是在运行时出现了丢帧,还可能是UI线程被阻塞.首先来一下丢帧现象,android每16ms会对界面进行一次渲染,如果app的绘制.计算等超过了16ms那么只能等下一个16ms才能 ...

  2. android TextView SetText卡顿原因

    [android TextView SetText卡顿原因] 不要用wrap_content即可. 参考:http://blog.csdn.net/self_study/article/details ...

  3. H5:加载原理,慢加载和卡顿原因分析,

    前端H5工作原理: 请求和显示原理 H5页面卡顿原因分析: 1.动画太多:渲染重绘占用GPU 2.页面操作导致重绘频繁 3.页面元素复杂:资源类标签太多(图像/视频/dom树太长) 4.内置webvi ...

  4. android中fragment卡顿的原因

    首页的ViewPager有十几个Fragment,在快速切换的时候,容易产生卡顿现象. 二.分析当ViewPager切换到当前的Fragment时,Fragment会加载布局并显示内容,如果用户这时快 ...

  5. 面试官:你的App卡顿过吗?你是如何监控的?

    一.故事开始 面试官:平时开发中有遇到卡顿问题吗?你一般是如何监控的? 来面试的小伙:额...没有遇到过卡顿问题,我平时写的代码质量比较高,不会出现卡顿. 面试官:... 这回答似乎没啥问题,但是如果 ...

  6. Android 界面滑动卡顿分析与解决方案(入门)

    Android 界面滑动卡顿分析与解决方案(入门) 导致Android界面滑动卡顿主要有两个原因: 1.UI线程(main)有耗时操作 2.视图渲染时间过长,导致卡顿 目前只讲第1点,第二点相对比较复 ...

  7. Android中Java反射技术的使用示例

    import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Metho ...

  8. android中倒计时控件CountDownTimer分析

    android中倒计时控件CountDownTimer分析 1 示例代码 new CountDownTimer(10000, 1000) { public void onTick(long milli ...

  9. Android 教你如何发现 APP 卡顿

    最近部门打算优化下 APP 在低端机上的卡顿情况,既然想优化,就必须获取卡顿情况,那么如何获取卡顿情况就是本文目的. 一般主线程过多的 UI 绘制.大量的 IO 操作或是大量的计算操作占用 CPU,导 ...

随机推荐

  1. golang 如何判断变量的类型

    本文介绍两种用于判断变量类型的方式. 方法一 package main import ( "fmt" ) func main() { v1 := "123456" ...

  2. nginx信号量

    nginx信号说明相关说明 信号名称 作用 TERM,INT 快速关闭 QUIT 从容关闭 HUP 重新加载配置,用新的配置开始新的工作进程,从容关闭旧的工作进程 USR1 重新打开日志文件 USR2 ...

  3. asp.net如何使用cookie(创建、保存、读取)

    Cookie的用法也和ASP中差不多.比如我们建立一个名为aspcn,值为大众的cookie HttpCookie cookie = new HttpCookie("aspcn") ...

  4. 【Spring学习笔记-MVC-12】Spring MVC视图解析器之ResourceBundleViewResolver

    场景 当我们设计程序界面的时候,中国人希望界面是中文,而美国人希望界面是英文. 我们当然希望后台代码不需改变,系统能够通过配置文件配置,来自己觉得是显示中文界面还是英文界面. 这是,Spring mv ...

  5. 【ActiveMQ入门-5】ActiveMQ学习-Queue与Topic的比较

    Queue与Topic的比较 1.JMS Queue执行load balancer语义: 一条消息仅能被一个consumer收到. 如果在message发送的时候没有可用的consumer,那么它将被 ...

  6. Linux系统文件名字体不同的颜色都代表什么

    Linux系统文件名字体不同的颜色都代表什么   在Linux中,文件的颜色都是有含义的.   其中, Linux中文件名颜色不同,代表文件类型不一样.   如下所示: www.2cto.com   ...

  7. jQuery的selector和context属性

    从jQuery1.3开始添加了这2个属性. 现在我们来看看那这2个属性的用法. selector属性是一个字符串.存储的字符串是选择器. HTML代码: <div class="guo ...

  8. 小朋友学Python(3)

    运算符 一.基本运算符 Python运算符多数与C/C++/Java类似,但有少数不一样. “/”表示求商,“//”求商的整数部分.11 / 2 = 5.500000, 11 // 2 = 5 “** ...

  9. 分水岭算法(理论+opencv实现)

    分水岭算法理论 从意思上就知道通过用水来进行分类,学术上说什么基于拓扑结构的形态学...其实就是根据把图像比作一副地貌,然后通过最低点和最高点去分类! 原始的分水岭: 就是上面说的方式,接下来用一幅图 ...

  10. C# List<string>和ArrayList用指定的分隔符分隔成字符串

    原文地址:https://www.cnblogs.com/ahwwmb/p/4166707.html 串联字符串数组的所有元素,其中在每个元素之间使用指定的分隔符 List<string> ...