装修公司的网站怎么做,盛大印刷公司网页设计,嘉定网站设计怎么样,爱演示网什么是UI卡顿#xff1f;
在Android系统中#xff0c;我们知道UI线程负责我们所有视图的布局#xff0c;渲染工作#xff0c;UI在更新期间#xff0c;如果UI线程的执行时间超过16ms#xff0c;则会产生丢帧的现象#xff0c;而大量的丢帧就会造成卡顿#xff0c;影响用…什么是UI卡顿
在Android系统中我们知道UI线程负责我们所有视图的布局渲染工作UI在更新期间如果UI线程的执行时间超过16ms则会产生丢帧的现象而大量的丢帧就会造成卡顿影响用户体验。
UI卡顿产生的原因
在UI线程中做了大量的耗时操作导致了UI刷新工作的阻塞。系统CPU资源紧张APP所能分配的时间片减少。Ardroid虚拟机频繁的执行GC操作导致占用了大量的系统资源同时也会导致UI线程的短暂停顿从而产生卡顿。代码编写不当产生了过度绘制导致CPU执行时间变长早场卡顿。
从上可知大部分的卡顿原因都产生于代码编写不当导致而这类问题都可以通过各种优化方案进行优化所以我们需要做的就是尽可能准确的找到卡顿的原因定位到准确的代码模块最好是能定位到哪个方法导致卡顿这样我们APP的性能就能得到很大的提升。
UI卡顿方案
开发阶段
在开发阶段我们可以借助开发工具为我们提供的各种便利来有效的识别卡顿如下
System Trace
具体使用可以看blog.csdn.net/u011578734/… 写的文章。
Android CPU Profiler
Android Studio CPU 性能剖析器可实时检查应用的 CPU 使用率和线程活动。你还可以检查方法跟踪记录、函数跟踪记录和系统跟踪记录中的详细信息。使用CPU profiler可以查看主线程中每个方法的耗时情况以及每个方法的调用栈可以很方便的分析卡顿产生的原因以及定位到具体的代码方法。
具体使用方法可以参考 blog.csdn.net/u011578734/…
线上UI卡顿检测方案
线上检测方案比较流行的是BlockCanary和WatchDog下面我们就看看它们是怎么做到检测UI卡顿的并反馈给开发人员。
BlockCanary
BlockCanary能检测到主线程的卡顿, 并将结果记录下来, 以友好的方式展示,很类似于LeakCanary的展示。
BlockCanary的使用很简单只要在Application中进行设置一下就可以如下
BlockCanary.install(this, new AppBlockCanaryContext()).start();AppBlockCanaryContext继承自BlockCanaryContext是对BlockCanary中各个参数进行配置的类
可配置参数如下
//卡顿阀值
int getConfigBlockThreshold();
boolean isNeedDisplay();
String getQualifier();
String getUid();
String getNetworkType();
Context getContext();
String getLogPath();
boolean zipLogFile(File[] src, File dest);
//可将卡顿日志上传到自己的服务
void uploadLogFile(File zippedFile);
String getStackFoldPrefix();
int getConfigDumpIntervalMillis();在某个消息执行时间超过设定的标准时会弹出通知进行提醒或者上传。
原理
熟悉Android的Handler机制的同学一定知道Handler中重要的组成部分looper并且应用的主线程只有一个Looper存在不管有多少handler最后都会回到这里。 我们注意到Looper.loop()中有这么一段代码
public static void loop() {...for (;;) {...// This must be in a local variable, in case a UI event sets the loggerPrinter logging me.mLogging;if (logging ! null) {logging.println( Dispatching to msg.target msg.callback : msg.what);}msg.target.dispatchMessage(msg);if (logging ! null) {logging.println( Finished to msg.target msg.callback);}...}
}注意到两个很关键的地方是logging.println( Dispatching to msg.target msg.callback : msg.what);和logging.println( Finished to msg.target msg.callback);这两行代码它调用的时机正好在dispatchMessage(msg)的前后而主线程卡也就是在dispatchMessage(msg)卡住了。
BlockCanary的流程图 BlockCanary就是通过替换系统的Printer来增加了一些我们想要的堆栈信息从而满足我们的需求。
替换原有的Printer是通过以下方法
Looper.getMainLooper().setMessageLogging(mainLooperPrinter);并在mainLooperPrinter中判断start和end来获取主线程dispatch该message的开始和结束时间并判定该时间超过阈值(如2000毫秒)为主线程卡慢发生并dump出各种信息提供开发者分析性能瓶颈。如下所示
Override
public void println(String x) {if (!mStartedPrinting) {mStartTimeMillis System.currentTimeMillis();mStartThreadTimeMillis SystemClock.currentThreadTimeMillis();mStartedPrinting true;startDump();} else {final long endTime System.currentTimeMillis();mStartedPrinting false;if (isBlock(endTime)) {notifyBlockEvent(endTime);}stopDump();}
}private boolean isBlock(long endTime) {return endTime - mStartTimeMillis mBlockThresholdMillis;
}BlockCanary dump的信息包括如下
基本信息安装包标示、机型、api等级、uid、CPU内核数、进程名、内存、版本号等
耗时信息实际耗时、主线程时钟耗时、卡顿开始时间和结束时间
CPU信息时间段内CPU是否忙时间段内的系统CPU/应用CPU占比I/O占CPU使用率
堆栈信息发生卡慢前的最近堆栈可以用来帮助定位卡慢发生的地方和重现路径获取系统状态信息是通过如下代码实现
threadStackSampler new ThreadStackSampler(Looper.getMainLooper().getThread(),sBlockCanaryContext.getConfigDumpIntervalMillis());
cpuSampler new CpuSampler(sBlockCanaryContext.getConfigDumpIntervalMillis());下面看一下ThreadStackSampler是怎么工作的
protected void doSample() {
// Log.d(BlockCanary, sample thread stack: [ mThreadStackEntries.size() , mMaxEntryCount ]);StringBuilder stringBuilder new StringBuilder();// Fetch thread stack infofor (StackTraceElement stackTraceElement : mThread.getStackTrace()) {stringBuilder.append(stackTraceElement.toString()).append(Block.SEPARATOR);}// Eliminate obsolete entrysynchronized (mThreadStackEntries) {if (mThreadStackEntries.size() mMaxEntryCount mMaxEntryCount 0) {mThreadStackEntries.remove(mThreadStackEntries.keySet().iterator().next());}mThreadStackEntries.put(System.currentTimeMillis(), stringBuilder.toString());}
}直接去拿主线程的栈信息, 每半秒去拿一次, 记录下来, 如果发生卡顿就显之显示出来 拿CPU的信息较麻烦, 从/proc/stat下面拿实时的CPU状态, 再从/proc/ mPid /stat中读取进程时间, 再计算各CPU时间占比和CPU的工作状态.
基于系统WatchDog原理来实现
启动一个卡顿检测线程该线程定期的向UI线程发送一条延迟消息执行一个标志位加1的操作如果规定时间内标志位没有变化则表示产生了卡顿。如果发生了变化则代表没有长时间卡顿我们重新执行延迟消息即可。
public class WatchDog {private final static String TAG budaye;//一个标志private static final int TICK_INIT_VALUE 0;private volatile int mTick TICK_INIT_VALUE;//任务执行间隔public final int DELAY_TIME 4000;//UI线程Handler对象private Handler mHandler new Handler(Looper.getMainLooper());//性能监控线程private HandlerThread mWatchDogThread new HandlerThread(WatchDogThread);//性能监控线程Handler对象private Handler mWatchDogHandler;//定期执行的任务private Runnable mDogRunnable new Runnable() {Overridepublic void run() {if (null mHandler) {Log.e(TAG, handler is null);return;}mHandler.post(new Runnable() {Overridepublic void run() {//UI线程中执行mTick;}});try {//线程休眠时间为检测任务的时间间隔Thread.sleep(DELAY_TIME);} catch (InterruptedException e) {e.printStackTrace();}//当mTick没有自增时表示产生了卡顿这时打印UI线程的堆栈if (TICK_INIT_VALUE mTick) {StringBuilder sb new StringBuilder();//打印堆栈信息StackTraceElement[] stackTrace Looper.getMainLooper().getThread().getStackTrace();for (StackTraceElement s : stackTrace) {sb.append(s.toString() \n);}Log.d(TAG, sb.toString());} else {mTick TICK_INIT_VALUE;}mWatchDogHandler.postDelayed(mDogRunnable, DELAY_TIME);}};/*** 卡顿监控工作start方法*/public void startWork(){mWatchDogThread.start();mWatchDogHandler new Handler(mWatchDogThread.getLooper());mWatchDogHandler.postDelayed(mDogRunnable, DELAY_TIME);}
}调用startWork即可开启卡顿检测。
为了帮助到大家更好的全面清晰的掌握好性能优化准备了相关的核心笔记还该底层逻辑https://qr18.cn/FVlo89
性能优化核心笔记https://qr18.cn/FVlo89
启动优化 内存优化 UI优化 网络优化 Bitmap优化与图片压缩优化https://qr18.cn/FVlo89 多线程并发优化与数据传输效率优化 体积包优化
《Android 性能监控框架》https://qr18.cn/FVlo89 《Android Framework学习手册》https://qr18.cn/AQpN4J
开机Init 进程开机启动 Zygote 进程开机启动 SystemServer 进程Binder 驱动AMS 的启动过程PMS 的启动过程Launcher 的启动过程Android 四大组件Android 系统服务 - Input 事件的分发过程Android 底层渲染 - 屏幕刷新机制源码分析Android 源码分析实战