栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

【android每日一问】怎么检测UI卡顿,移动客户端开发面试

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【android每日一问】怎么检测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 logger
Printer 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 info
for (StackTraceElement stackTraceElement : mThread.getStackTrace()) {
stringBuilder.append(stackTraceElement.toString())
// Fetch thread stack info
for (StackTraceElement stackTraceElement : mThread.getStackTrace()) {
stringBuilder.append(stackTraceElement.toString())

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/716887.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号