热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【android每日一问】怎么检测UI卡顿,移动端h5页面加载慢

…}}注意到两个很关键的地方是logging.println(Dispatchingtomsg.targetmsg.callback:msg.what);和


}
}

注意到两个很关键的地方是logging.println(">>>>> Dispatching to " &#43; msg.target &#43; " " &#43; msg.callback &#43; ": " &#43; msg.what);logging.println![](https://www.hualigs.cn/image/61dba891ed8ee.jpg) ("<<<<这两行代码&#xff0c;它调用的时机正好在dispatchMessage(msg)的前后&#xff0c;而主线程卡也就是在dispatchMessage(msg)卡住了。

BlockCanary的流程图

&#xff08;图片来自网络&#xff09;

blockcanary_flow.png

BlockCanary就是通过替换系统的Printer来增加了一些我们想要的堆栈信息&#xff0c;从而满足我们的需求。

替换原有的Printer是通过以下方法&#xff1a;

Looper.getMainLooper().setMessageLogging(mainLooperPrinter);

并在mainLooperPrinter中判断start和end&#xff0c;来获取主线程dispatch该message的开始和结束时间&#xff0c;并判定该时间超过阈值(如2000毫秒)为主线程卡慢发生&#xff0c;并dump出各种信息&#xff0c;提供开发者分析性能瓶颈。如下所示&#xff1a;

&#64;Override
public void println(String x) {
if (!mStartedPrinting) {
mStartTimeMillis &#61; System.currentTimeMillis();
mStartThreadTimeMillis &#61; SystemClock.currentThreadTimeMillis();
mStartedPrinting &#61; true;
startDump();
} else {
final long endTime &#61; System.currentTimeMillis();
mStartedPrinting &#61; false;
if (isBlock(endTime)) {
notifyBlockEvent(endTime);
}
stopDump();
}
}

private boolean isBlock(long endTime) {
return endTime - mStartTimeMillis > mBlockThresholdMillis;
}

  • BlockCanary dump的信息包括如下&#xff1a;

基本信息&#xff1a;安装包标示、机型、api等级、uid、CPU内核数、进程名、内存、版本号等
耗时信息&#xff1a;实际耗时、主线程时钟耗时、卡顿开始时间和结束时间
CPU信息&#xff1a;时间段内CPU是否忙&#xff0c;时间段内的系统CPU/应用CPU占比&#xff0c;I/O占CPU使用率
堆栈信息&#xff1a;发生卡慢前的最近堆栈&#xff0c;可以用来帮助定位卡慢发生的地方和重现路径

  • 获取系统状态信息是通过如下代码实现&#xff1a;

threadStackSampler &#61; new ThreadStackSampler(Looper.getMainLooper().getThread(),
sBlockCanaryContext.getConfigDumpIntervalMillis());
cpuSampler &#61; new CpuSampler(sBlockCanaryContext.getConfigDumpIntervalMillis());

下面看一下ThreadStackSampler是怎么工作的&#xff1f;

protected void doSample() {
// Log.d(“BlockCanary”, “sample thread stack: [” &#43; mThreadStackEntries.size() &#43; ", " &#43; mMaxEntryCount &#43; “]”);
StringBuilder stringBuilder &#61; new StringBuilder();

// Fetch thread stack info
for (StackTraceElement stackTraceElement : mThread.getStackTrace()) {
stringBuilder.append(stackTraceElement.toString())
.append(Block.SEPARATOR);
}
// Eliminate obsolete entry
synchronized (mThreadStackEntries) {
if (mThreadStackEntries.size() &#61;&#61; mMaxEntryCount && mMaxEntryCount > 0) {
mThreadStackEntries.remove(mThreadStackEntries.keySet().iterator().next());
}
mThreadStackEntries.put(System.currentTimeMillis(), stringBuilder.toString());
}
}

直接去拿主线程的栈信息, 每半秒去拿一次, 记录下来, 如果发生卡顿就显之显示出来 拿CPU的信息较麻烦, 从/proc/stat下面拿实时的CPU状态, 再从/proc/" &#43; mPid &#43; "/stat中读取进程时间, 再计算各CPU时间占比和CPU的工作状态.

基于系统WatchDog原理来实现


  • 启动一个卡顿检测线程&#xff0c;该线程定期的向UI线程发送一条延迟消息&#xff0c;执行一个标志位加1的操作&#xff0c;如果规定时间内&#xff0c;标志位没有变化&#xff0c;则表示产生了卡顿。如果发生了变化&#xff0c;则代表没有长时间卡顿&#xff0c;我们重新执行延迟消息即可。

public class WatchDog {
private final static String TAG &#61; “budaye”;
//一个标志
private static final int TICK_INIT_VALUE &#61; 0;
private volatile int mTick &#61; TICK_INIT_VALUE;
//任务执行间隔
public final int DELAY_TIME &#61; 4000;
//UI线程Handler对象
private Handler mHandler &#61; new Handler(Looper.getMainLooper());
//性能监控线程
private HandlerThread mWatchDogThread &#61; new HandlerThread(“WatchDogThread”);
//性能监控线程Handler对象
private Handler mWatchDogHandler;

//定期执行的任务
private Runnable mDogRunnable &#61; new Runnable() {
&#64;Override
public void run() {
if (null &#61;&#61; mHandler) {
Log.e(TAG, “handler is null”);
return;
}
mHandler.post(new Runnable() {
&#64;Override
public void run() {//UI线程中执行
mTick&#43;&#43;;
}
});
try {
//线程休眠时间为检测任务的时间间隔
Thread.sleep(DELAY_TIME);
} catch (InterruptedException e) {
e.printStackTrace();
}
//当mTick没有自增时&#xff0c;表示产生了卡顿&#xff0c;这时打印UI线程的堆栈
if (TICK_INIT_VALUE &#61;&#61; mTick) {
StringBuilder sb &#61; new StringBuilder();
//打印堆栈信息
StackTraceElement[] stackTrace &#61; Looper.getMainLooper().getThread().getStackTrace();
for (StackTraceElement s : stackTrace) {
sb.append(s.toString() &#43; “\n”);
}
Log.d(TAG, sb.toString());
} else {
mTick &#61; TICK_INIT_VALUE;
}
mWatchDogHandler.postDelayed(mDogRunnable, DELAY_TIME);
}
};
/**

  • 卡顿监控工作start方法
    /
    public void startWork(){
    mWatchDogThread.start();
    mWatchDogHandler &#61; new Handler(mWatchDogThread.getLooper());
    E);
    }
    };
    /
    *
  • 卡顿监控工作start方法
    */
    public void startWork(){
    mWatchDogThread.start();
    mWatchDogHandler &#61; new Handler(mWatchDogThread.getLooper());

推荐阅读
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 在Kubernetes上部署JupyterHub的步骤和实验依赖
    本文介绍了在Kubernetes上部署JupyterHub的步骤和实验所需的依赖,包括安装Docker和K8s,使用kubeadm进行安装,以及更新下载的镜像等。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • 树莓派语音控制的配置方法和步骤
    本文介绍了在树莓派上实现语音控制的配置方法和步骤。首先感谢博主Eoman的帮助,文章参考了他的内容。树莓派的配置需要通过sudo raspi-config进行,然后使用Eoman的控制方法,即安装wiringPi库并编写控制引脚的脚本。具体的安装步骤和脚本编写方法在文章中详细介绍。 ... [详细]
  • 在Docker中,将主机目录挂载到容器中作为volume使用时,常常会遇到文件权限问题。这是因为容器内外的UID不同所导致的。本文介绍了解决这个问题的方法,包括使用gosu和suexec工具以及在Dockerfile中配置volume的权限。通过这些方法,可以避免在使用Docker时出现无写权限的情况。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • 本文研究了使用条件对抗网络进行图片到图片翻译的方法,并提出了一种通用的解决方案。通过学习输入图像到输出图像的映射和训练相应的损失函数,我们可以解决需要不同损失函数公式的问题。实验证明该方法在合成图片、重构目标和给图片着色等多个问题上都很有效。这项工作的重要发现是不再需要人为构建映射函数和损失函数,同时能够得出合理的结果。本文的研究对于图片处理、计算机图片合成和计算机视觉等领域具有重要意义。 ... [详细]
  • imx6ull开发板驱动MT7601U无线网卡的方法和步骤详解
    本文详细介绍了在imx6ull开发板上驱动MT7601U无线网卡的方法和步骤。首先介绍了开发环境和硬件平台,然后说明了MT7601U驱动已经集成在linux内核的linux-4.x.x/drivers/net/wireless/mediatek/mt7601u文件中。接着介绍了移植mt7601u驱动的过程,包括编译内核和配置设备驱动。最后,列举了关键词和相关信息供读者参考。 ... [详细]
  • HTML学习02 图像标签的使用和属性
    本文介绍了HTML中图像标签的使用和属性,包括定义图像、定义图像地图、使用源属性和替换文本属性。同时提供了相关实例和注意事项,帮助读者更好地理解和应用图像标签。 ... [详细]
  • flowable工作流 流程变量_信也科技工作流平台的技术实践
    1背景随着公司业务发展及内部业务流程诉求的增长,目前信息化系统不能够很好满足期望,主要体现如下:目前OA流程引擎无法满足企业特定业务流程需求,且移动端体 ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • React项目中运用React技巧解决实际问题的总结
    本文总结了在React项目中如何运用React技巧解决一些实际问题,包括取消请求和页面卸载的关联,利用useEffect和AbortController等技术实现请求的取消。文章中的代码是简化后的例子,但思想是相通的。 ... [详细]
author-avatar
8023pxeb_256
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有