基于Handler机制实现。
一、卡顿原因
在Android中,UI线程负责执行UI视图的布局、渲染等工作。如果UI线程执行时每帧渲染时间超过了16ms,就会产生卡帧现象,俗称丢帧,大量的丢帧就会造成卡顿现象,影响用户体验,严重时导致屏幕卡死,甚至黑屏。
产生的原因如下:
- CPU资源紧张,分配给APP主线程(UI线程)的CPU时间片减少;
- UI线程中执行了大量的耗时任务,导致了UI线程视图刷新工作的阻塞;
- Android虚拟机频繁执行GC操作导致的卡顿。由于GC会占用大量的系统资源,同时GC过程中会产生UI线程停顿,从而产生卡顿;
- 过度绘制产生卡顿。过度绘制会导致GPU执行时间变长,从而产生丢帧现象。
二、卡顿监控代码
监控逻辑
- 使用了一个工作线程mBlockThread来监控UI线程的卡顿;
- 每次Looper的loop方法对消息进行处理之前,我们添加一个定时监控器;
- 如果UI线程中的消息处理时间小于我们设定的阈值BLOCK_TMME,则取消已添加的定时器;
- 当UI线程执行耗时任务,超过我们设定的阈值时,就会执行mBlockRunnable这个Rnnable,在它的run方法中,打印出主线程卡顿时的代码堆栈;
- 我们把堆栈日志收集起来,进行归类分析,就可以定位到产生卡顿问题的具体代码行号了。
public class UICatonMonitoring {
private final static String TAG = "caton";
public final int BLOCK_TMME = 1000;
private HandlerThread mBlockThread = new HandlerThread("blockThread");
private Handler mHandler;private Runnable mBlockRunnable = new Runnable() {
@Override
public void run() {
StringBuilder sb = new StringBuilder();
StackTraceElement[] stackTrace = Looper.getMainLooper().getThread().getStackTrace();
for (StackTraceElement s : stackTrace) {
sb.append(s.toString() + "\n");
}
Log.d(TAG, sb.toString());
}
};
public void startWork(){
mBlockThread.start();
mHandler = new Handler(mBlockThread.getLooper());
Looper.getMainLooper().setMessageLogging(new Printer() {
private static final String START = ">>>>> Dispatching";
private static final String END = "<<<<< Finished";@Override
public void println(String x) {
if (x.startsWith(START)) {
startMonitor();
}
if (x.startsWith(END)) {
removeMonitor();
}
}
});
}private void startMonitor() {
mHandler.postDelayed(mBlockRunnable, BLOCK_TMME);
}
private void removeMonitor() {
mHandler.removeCallbacks(mBlockRunnable);
}
}