1,卡顿的原因
界面绘制:主要原因是绘制的层级深、页面复杂、刷新不合理,由于这些原因导致卡顿的场景更多出现在UI和启动后的初始界面以及跳转到页面的绘制上。
数据处理:导致这种卡顿场景的原因是数据处理量太大,一般分为三种情况,一是数据处理在UI线程(这种应该避免),二是数据处理占用CPU高,导致主线程拿不到时间片,三是内存增加导致GC频繁,从而引起卡顿。
先行了解下Android的UI刷新机制:Android Project Butter分析
2,Profile GPU Rendering
从应用层绘制一个页面(View),主要有三个过程:CPU准备数据→GPU从数据缓存列表获取数据→Display设备绘制,这三个过程的耗时可以通过一个手机开发辅助工具查看:Profile GPU Rendering。Profile GPU Rendering是系统提供的UI绘制检测工具,在设置开发者选项中可以打开,打开后显示如下:
它是一个图形监测工具,能实时反应当前绘制的耗时。横轴表示时间,纵轴表示每一帧的耗时(单位为ms)。随着时间推移,从左到右的刷新呈现。提供了一个标准的耗时,如果高于标准耗时,表示当前这一帧丢失。
每一条柱状图都由4种颜色组成:红、黄、蓝、紫,这些线对应每一帧在不同阶段的实际耗时。
蓝色:代表测量绘制的时间,就是记录了在屏幕上更新视图需要花费的时间,也可以理解为执行每一个View的onDraw方法,创建或者更新每一个View的Display List对象。在蓝色的线很高时,有可能是因为需要重新绘制,或者自定义视图的onDraw函数处理事情太多。
红色:代表执行的时间,这部分是Android进行2D渲染通过API将数据发送到GPU,最终在屏幕上显示出来。当红色的线非常高时,可能是由重新提交了视图而导致的。
橙色:表示处理时间,或者是CPU告诉GPU渲染一帧的地方,这是一个阻塞调用,因为CPU会一直等待GPU发出接到命令的回复,如果柱状图很高,就意味着GPU太繁忙了。
·绿色(紫色):表示将资源转移到渲染线程的时间,只有Android 4.0及以上版本才会提供。
任何时候超过绿线(警戒线,对应时长16ms),就有可能丢失一帧的内容,虽然对于大部分应用来说,丢失几帧确实感觉不出卡顿,但保持UI流畅的关键就在于让这些垂直的柱状条尽可能地保持在绿线下面。但是这个Profile GPU Rendering工具是提供我们肉眼看一个页面的绘制情况的直观表现,一般并不会帮我们找到代码中哪些不合理的地方,需要其它工具才可以。
3,TraceView
TraceView是Android SDK提供的性能检测工具,它是一个图形工具,可以检测Android应用层和Framework层的代码执行情况,形成一个图表,从中可以分析出每个方法的执行时间。
1,TraceView的GUI使用方式:
(1)使用方式:AndroidStudio——Tools——Android——Android Device Monitor
(2)单击Strart Method Profiling按钮,如下图:
(3)开启后,在需要检测的路径上做一些操作,例如滑动列表,跳转Activity等等,然后点击Stop Metod Profiling,结果出生成如下图所示的结果图表:
2,TraceView的代码使用方式:
上面的GUI使用方式不太明确,也不精确,很难控制操作的路径,有个更好的方式就是在代码中控制执行的流程,在android.os.Debug类中提供了startMethodTracing和stopMethodTracing方法,前面用来设置开始检测的位置,后面设置结束检测的位置,例如:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRecyclerView = (RecyclerView) findViewById(R.id.recyclerView);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(layoutManager);
mDataSource = new ArrayList<>();
myAdapter = new MyAdapter(mDataSource);
mRecyclerView.setAdapter(myAdapter);
Debug.startMethodTracing("ui_trace");
addDataSource();
Debug.stopMethodTracing();
myAdapter.refreshData(mDataSource);
}
private void addDataSource() {
for (int i = 0; i < 100; i++) {
mDataSource.add(new ItemBean(R.mipmap.ic_launcher, "This is Data No." + i));
}
}
注意:在startMethodTracing方法中需要传入一个文件名,那么系统执行到这段代码时,会自动在指定的目录下创建一个.trace的文件。因为涉及了IO操作,还需要WRITE_EXTERNAL_STORAGE权限。
3,TraceView的面板
Traceview视图分两部分,上半部分为时间片面板(Timeline Panel),下半部分为分析面板(Profile Panel)。
X轴表示时间消耗,单位为毫秒(ms),Y轴表示各个线程,每个线程中的不同方法使用了不同的颜色来表示,颜色占用面积越宽,表示该方法占用CPU时间越长。
时间片面板可以放大/缩小,也可以指定区域放到最大,方便查看具体的过程,一般优先选择放大耗时严重的区域。
使用TraceView查看耗时,主要关注Calls+Recur Calls/Total和Cpu Time/Call这两个值,也就是关注调用次数多和耗时久的方法,然后优化这些方法的逻辑和调用次数,减少耗时。
注意:RealTime与cputime区别为:因为RealTime包括了CPU的上下文切换、阻塞、GC等,所以RealTime方法的实际执行时间要比CPU Time稍微长一点。
4,Systrace
Systrace是Android 4.1及以上版本提供的性能数据采样和分析工具。它可以帮助开发者收集Android关键子系统(如surfaceflinger、WindowManagerService等Framework部分关键模块、服务,View系统等)的运行信息,从而帮助开发者更直观地分析系统瓶颈,改进性能。Systrace的功能包括跟踪系统的I/O操作、内核工作队列、CPU负载等,在UI显示性能分析上提供很好的数据,特别是在动画播放不流畅、渲染卡等问题上。Systrace工具可以跟踪、收集、检查定时信息,可以很直观地查看CPU周期消耗的具体时间,显示每个线程和进程的跟踪信息,使用不同颜色来突出问题的严重性,并提供如何解决这些问题的建议。
注意:由于Systrace是以系统的角度返回一些信息,并不能定位到具体耗时的方法,要进一步获取CPU满负荷运行的原因,就需要使用前面介绍过的工具Traceview。
1,Systrace的GUI使用方式
(1)使用方式:AndroidStudio——Tools——Android——Android Device Monitor
(2)点击Systrace,设置好trace文件保存的路径和检测的时间,默认是5s
(3)然后在应用中进行一些操作
(4)在设置的时间结束后,会在指定的目录下生成一个trace.html的文件,这个文件需要使用Google Chrome浏览器打开查看。
2,Systrace的代码使用方式
Systrace不会追踪应用的所有工作,所以在有需求的情况下,需要添加要追踪的代码部分。可以通过Trace类来实现这个功能,即Trace.beginSection()与Trace.endSection()之间的代码工作会一直被追踪。
注意:
在Trace被嵌套在另一个Trace中时,endSection()方法只会结束离它最近的一个beginSection(String),即在一个Trace的过程中是无法中断其他Trace的。所以要保证endSection()与beginSection(String)调用次数匹配。
Trace的begin与end必须在同一线程中执行。
public class MainActivity extends AppCompatActivity {
private RecyclerView mRecyclerView;
private MyAdapter myAdapter;
private List mDataSource;
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRecyclerView = (RecyclerView) findViewById(R.id.recyclerView);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(layoutManager);
mDataSource = new ArrayList<>();
myAdapter = new MyAdapter(mDataSource);
mRecyclerView.setAdapter(myAdapter);
Trace.beginSection("systrace");
SystemClock.sleep(2000);
addDataSource();
Trace.endSection();
myAdapter.refreshData(mDataSource);
}
private void addDataSource() {
for (int i = 0; i < 100; i++) {
mDataSource.add(new ItemBean(R.mipmap.ic_launcher, "This is Data No." + i));
}
}
}
上面就是Systrace生成的.html的文件在chrome中打开后的样子,比较长,但是我们可以通过键盘上的awsd四个键移动和缩放视图,其中我们比较关注的是Alerts和Frame两个数据。
(1)Alerts
图中发现Alerts上有些数据,单击每个数据都可以在下面看到每个数据代表的含义和描述。
(2)Frame
每个应用都有一行专门显示frame,每一帧就显示为一个灰色的圆圈。当显示为黄色或者红色时,它的渲染时间超过了16.6ms(即达不到60fps的水准)。使用W键放大,看看这一帧的渲染过程中系统到底做了什么,同时它会将任何它认为性能有问题的东西都高亮警告,并提示要怎么优化。
但是,如果想知道UI线程怎么会花费这么多时间的话,就需要使用TraceView,来分析具体是哪些函数在消耗时间。
参考资料:
Android Project Butter分析
Android 编程下的 TraceView 简介及其案例实战
Android 性能优化 二 TraceView工具的使用
Android系统性能调优工具介绍
使用Systrace分析UI性能