ANR灵魂拷问:四大组件中的onCreate-onReceive方法中Thread-sleep(),会产生几个ANR-

findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
sleepTest();
}
});

sleepTest方法详情

public void sleepTest(){
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
Button button = findViewById(R.id.btn);
button.setText(“jackie”);
}
},7000);
try {
Thread.sleep(21_000);
Log.i(TAG, “onCreate: ====2”);
} catch (InterruptedEx​
ception e) {
e.printStackTrace();
}
}

点击按钮,是否会发生ANR,答案是不会。这就是耗时操作不一定会导致没有响应,和onCreate方法中进行sleep同理,但是如果你点击了按钮之后又点击了返回键,这个时候就会出现ANR了,因为主线程被休眠了,你点击了按键在5s内没有响应,这个时候就会出现ANR。

Service

在Service中的onCreate方法,默认创建的启动Activity创建的是前台服务(20s),所以会造成ANR。

@Override
public void onCreate() {
super.onCreate();
try {
Log.i(TAG, “onCreate: ==1=”);
//Thread.sleep(19000); //不发生anr
Thread.sleep(21000); //发生anr
Log.i(TAG, “onCreate: 2”);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

ContentProvider

@Override
public boolean onCreate() {
Log.i(TAG, “onCreate: =1==”);
try {
Thread.sleep(21_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.i(TAG, “onCreate: =2===”);
return false;
}

这里也不会产生ANR,前面说到ContentProvider在publish过超时10s,”ActivityManager”线程中的AMS.MainHandler收到CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG消息时触发。而通过查看ContentProvider源码和启动流程,我们可以看到onCreate方法在publish之前执行,所以在onCreate方法中并不会造成ANR。

BroadcastReceiver

发送广播

Intent intent = new Intent(“android.intent.jackie”);
//设置为前台广播,默认是后台广播
intent.setPackage(getPackageName()); //一定要设置包名,否则无法显示anr,具体暂不深究
//intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
Test2Activity.this.sendBroadcast(intent);

onReceive中执行耗时

public class TestBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = “TestBroadcastReceiver”;
@Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG, “onReceive: =1==”);
try {
//这里接受的都是前台广播
//Thread.sleep(9000); //不发生anr
Thread.sleep(21_000); //发生anr
//这里接受的都是后台广播
//Thread.sleep(65000); //发生anr
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.i(TAG, “onReceive: =2==”);
}
}

对于前台广播,则超时为BROADCAST_FG_TIMEOUT = 10s,后台广播的耗时是60s。所以这里会产生ANR。注意发送广播的时候要添加intent的包名。

Application

顺便做一下Application的测试

@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, “onCreate: a=”);
try {
Thread.sleep(21_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.i(TAG, “onCreate: =b=”);

}

并不会发生ANR,Android中的ANR并没有这个场景。

小结

除了输入事件造成的ANR,我们可以把其他ANR比作是引爆炸弹,那么整个流程包含三部分组成:

  1. 埋定时炸弹:中控系统(system_server进程)启动倒计时,在规定时间内如果目标(应用进程)没有干完所有的活,则中控系统会定向炸毁(杀进程)目标。
  2. 拆炸弹:在规定的时间内干完工地的所有活,并及时向中控系统报告完成,请求解除定时炸弹,则幸免于难。
  3. 引爆炸弹:中控系统立即封装现场,抓取快照,搜集目标执行慢的罪证(traces),便于后续的案件侦破(调试分析),最后是炸毁目标。

ANR的流程可以分为,埋炸弹->拆炸弹->引爆炸弹,如果埋的炸弹在一定时间内没有被拆除,就是**发送延迟消息一定时间内没有被移除,那就会引爆(触发),产生ANR。**详细分析可以查看该文章。

输入事件造成的ANR(特殊)

再来一个灵魂拷问:

在一个Activity中一个按钮点击事件中设置Thread.sleep(10_000),如果连续点击两次,会不会造成ANR,如果连续点击三次呢?

findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
Thread.sleep(10_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});

在Android29上(之前的版本并没有测试,预计应该一样的),连续点击两次并不会造成ANR,对于input事件,如果仅仅点击没有后续的点击输入事件不会造成ANR,可我们不是连续点击了两次了吗?因为第一次点击事件已经被消费了,在onClick中才进行sleep,然后我们点击第二次,这个时候只有一个input事件,没有后续的点击输入,所以不会造成ANR。

而如果我们连续点击三次,除去第一次被消费掉,这个时候就会有连续的input输入,会产生ANR。如果我们把Thread.sleep写在onTouch中,只要一次点击就行,因为该方法会被回调两次,down和up是两次input时间。 注意:如果是按键事件(假如点击返回键),如果主线程在sleep(),这个时候点击了返回键,5s没响应,直接抛出ANR。

Android R上的修改

在Android R(11)上,对于上面的输入事件造成的ANR的那个问题,只需要连续两次点击就可以造成ANR,该版本上对input机制做了较大的调整,有兴趣可以自行研究一下。

如何手动设计一个ANR监听
FileObserver

Android中发送ANR之后,Android系统会将ANR Log输出至/data/anr/trace.txt,**本方案就是监听trace.txt文件。**在Android中可以通过FileObserver来实现对文件的监听,FileObserver是一个用于监听文件的访问,创建,修改,删除,移动等操作的监听器。但是在android5.0的普通用户app,不一定有权限来实现对文件的监听,一些系统的app可以用该方式实现。

public class ANRFileObserver extends FileObserver {

public ANRFileObserver(String path) {//data/anr/
super(path);
}

public ANRFileObserver(String path, int mask) {
super(path, mask);
}

@Override
public void onEvent(int event, @Nullable String path) {
switch (event)
{
case FileObserver.ACCESS://文件被访问
Log.i(“Jackie”, "ACCESS: " + path);
break;
case FileObserver.ATTRIB://文件属性被修改,如 chmod、chown、touch 等
Log.i(“Jackie”, "ATTRIB: " + path);
break;
case FileObserver.CLOSE_NOWRITE://不可写文件被 close
Log.i(“Jackie”, "CLOSE_NOWRITE: " + path);
break;
case FileObserver.CLOSE_WRITE://可写文件被 close
Log.i(“Jackie”, "CLOSE_WRITE: " + path);
break;
case FileObserver.CREATE://创建新文件
Log.i(“Jackie”, "CREATE: " + path);
break;
case FileObserver.DELETE:// 文件被删除,如 rm
Log.i(“Jackie”, "DELETE: " + path);
break;
case FileObserver.DELETE_SELF:// 自删除,即一个可执行文件在执行时删除自己
Log.i(“Jackie”, "DELETE_SELF: " + path);
break;
case FileObserver.MODIFY://文件被修改
Log.i(“Jackie”, "MODIFY: " + path);
break;
case FileObserver.MOVE_SELF://自移动,即一个可执行文件在执行时移动自己
Log.i(“Jackie”, "MOVE_SELF: " + path);
break;
case FileObserver.MOVED_FROM://文件被移走,如 mv
Log.i(“Jackie”, "MOVED_FROM: " + path);
break;
case FileObserver.MOVED_TO://文件被移来,如 mv、cp
Log.i(“Jackie”, "MOVED_TO: " + path);
break;
case FileObserver.OPEN://文件被 open
Log.i(“Jackie”, "OPEN: " + path);
break;
default:
//CLOSE : 文件被关闭,等同于(IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)
//ALL_EVENTS : 包括上面的所有事件
Log.i(“Jackie”, “DEFAULT(” + event + "): " + path);
break;
}
}
}

ANRWatchDog

我们知道android是基于消息处理机制的,整个程序的运行都是在一个loop死循环中,通过handler对消息进行分发,所以我们可以开启一个线程通过Handler定时发送消息到我们的主线程,如果该消息超过一定时间未被处理,我们就可以判断发生了ANR。

public class ANRWatchDog extends Thread {

private static final String TAG = “ANR”;
private int timeout = 5000;
private boolean ignoreDebugger = true;

static ANRWatchDog sWatchdog;

private Handler mainHandler = new Handler(Looper.getMainLooper());

private class ANRChecker implements Runnable{

private boolean mCompleted;
private long mStartTime;
//这个值记录了系统启动到当前时刻经过的时间
private long executeTime = SystemClock.uptimeMillis();

@Override
public void run() {
synchronized (ANRWatchDog.this) {
mCompleted = true;
executeTime = SystemClock.uptimeMillis();
}
}

void schedule() {
mCompleted = false;
mStartTime = SystemClock.uptimeMillis();
//每次都post到该队列的前面
mainHandler.postAtFrontOfQueue(this);
}

boolean isBlocked() {
return !mCompleted || executeTime - mStartTime >= 5000;
}
}

public interface ANRListener {
void onAnrHappened(String stackTraceInfo);
}

private ANRChecker anrChecker = new ANRChecker();

private ANRListener anrListener;

public void addANRListener(ANRListener listener){
this.anrListener = listener;
}

public static ANRWatchDog getInstance(){
if(sWatchdog == null){
sWatchdog = new ANRWatchDog();
}
return sWatchdog;
}

private ANRWatchDog(){
super(“ANR-WatchDog-Thread”);
}

@Override
public void run() {
//super.run();
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //设置为后台线程
while (true){
while (!isInterrupted()){
synchronized (this){
anrChecker.schedule();
long waitTime = timeout;
long start = SystemClock.uptimeMillis();
//确保sleep一定的时间,防止假唤醒
while (waitTime > 0){
try{
wait(waitTime);
} catch (InterruptedException e){
Log.w(TAG, "run: ", e);
}
waitTime = timeout - (SystemClock.uptimeMillis() - start);
}
if (!anrChecker.isBlocked()){
continue;
}
}
if (!ignoreDebugger && Debug.isDebuggerConnected()){
continue;
}
String stackTraceInfo = getStackTraceInfo();
if (anrListener != null){
anrListener.onAnrHappened(stackTraceInfo);
}
}
anrListener = null;

}

}
private String getStackTraceInfo() {
StringBuilder stringBuilder = new StringBuilder();
for (StackTraceElement stackTraceElement : Looper.getMainLooper().getThread().getStackTrace()) {
stringBuilder
.append(stackTraceElement.toString())
.append(“\r\n”);
}
return stringBuilder.toString();
}
}

模拟ANR的场景

void ANRTest(){
handler = new Handler();
ANRWatchDog.getInstance().addANRListener(new ANRWatchDog.ANRListener() {
@Override
public void onAnrHappened(String stackTraceInfo) {
Log.i(TAG, "onAnrHappened: =======发生了ANR: "+ stackTraceInfo);
}
});
ANRWatchDog.getInstance().start();
//模拟场景,通过handler发送消息sleep 10秒,我们ANR的检测工具就会检测到自己发送的消息超过5s没有被处理,发生了"ANR"了。
handler.postDelayed(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000 * 10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},3000);

最后

小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

资料⬅专栏获取
ch (InterruptedException e) {
e.printStackTrace();
}
}
},3000);

最后

小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。

[外链图片转存中…(img-Z3AiWNNn-1719086902126)]一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

资料⬅专栏获取

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/33029.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android MVP模式 入门

View:对应于布局文件 Model:业务逻辑和实体模型 Controllor:对应于Activity 看起来的确像那么回事,但是细细的想想这个View对应于布局文件,其实能做的事情特别少,实际上关于该布局文件中的数据绑定的操…

【转型指南】从软件测试到技术多面手

★ 导言 小艺是一位毕业于985的计算机硕士,工作多年,现在某大厂从事软件测试方面的管理工作。目前在工作中游刃有余,但面对技术的飞速变化和职业发展的不确定性,还是难免焦虑,正在积极思考如何进一步提升自己&#xff…

瑞_MongoDB_MongoDB副本集

文章目录 1 MongoDB副本集-Replica Sets1.1 简介1.2 副本集的三个角色1.3 副本集架构目标1.4 副本集的创建1.4.1 创建主节点1.4.2 创建副本节点1.4.3 创建仲裁节点1.4.4 初始化配置副本集和主节点1.4.5 查看副本集的配置内容 rs.conf()1.4.6 查看副本集状态1.4.7 添加副本从节点…

AIGC时代算法工程师的面试秘籍(2024.5.27-6.9第十五式) |【三年面试五年模拟】

写在前面 【三年面试五年模拟】旨在整理&挖掘AI算法工程师在实习/校招/社招时所需的干货知识点与面试方法,力求让读者在获得心仪offer的同时,增强技术基本面。也欢迎大家提出宝贵的优化建议,一起交流学习💪 欢迎大家关注Rocky…

ROS话题通信机制实操C++

ROS话题通信机制实操C 创建ROS工程发布方(二狗子)订阅方(翠花)编辑配置文件编译并执行注意订阅的第一条数据丢失 ROS话题通信的理论查阅ROS话题通信流程理论 在ROS话题通信机制实现中,ROS master 不需要实现,且连接的建立也已经被封装了,需要…

Applied Spatial Statistics(七):Python 中的空间回归

Applied Spatial Statistics(七):Python 中的空间回归 本笔记本演示了如何使用 pysal 的 spreg 库拟合空间滞后模型和空间误差模型。 OLS空间误差模型空间滞后模型三种模型的比较探索滞后模型中的直接和间接影响 import numpy as np impor…

【html】用html写一个博物馆首页

效果图&#xff1a; 二级导航&#xff1a; 源码&#xff1a; <!DOCTYPE html> <html lang"zh"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><l…

江协科技51单片机学习- p14 调试LCD1602显示屏

前言&#xff1a; 本文是根据哔哩哔哩网站上“江协科技51单片机”视频的学习笔记&#xff0c;在这里会记录下江协科技51单片机开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了江协科技51单片机教学视频和链接中的内容。 引用&#xff1a; 51单片机入门教程-2…

RabbitMQ消息队列 安装及基本介绍

一.MQ介绍 Message Queue &#xff08;MQ&#xff09;是一种跨进程的通信机制&#xff0c;用于在系统之间进行传递消息。MQ作为消息中间件&#xff0c;可以进行异步处理请求&#xff0c;从而减少请求响应时间和解耦 1.1 应用场景 1.1.1 系统之间通过MQ进行消息通信&#xff0…

leetcode 二分查找·系统掌握 x的平方根

题目&#xff1a; 题解 这题可以使用~01~泛型查找在0~x/2的范围内查找答案。 int mySqrt(int x) {long l0,rx,mid;while(l<r){mid(lr1)>>1;if(mid*mid>x)rmid-1;else lmid;}//因为一定有答案所以不用判定是否查找失败return l;}

Docker构建多平台镜像

docker的多架构镜像构建 目前很多服务器都是基于arm架构的&#xff0c;而现在大多数的docker镜像都是基于x86架构的。一种情况就是同样的代码编译成业务包做成镜像需要部署在不同架构的服务器上&#xff0c;这个时候我们就可以使用docker的多平台构建了。 以下操作是在centos7.…

深入探索 Nuxt3 Composables:掌握目录架构与内置API的高效应用

title: 深入探索 Nuxt3 Composables&#xff1a;掌握目录架构与内置API的高效应用 date: 2024/6/23 updated: 2024/6/23 author: cmdragon excerpt: 摘要&#xff1a;“本文深入探讨了Nuxt3 Composables&#xff0c;重点介绍了其目录架构和内置API的高效应用。通过学习本文&…

pcl::PointXYZRGBA造成点云无法显示

如果pcd文件没有rgba信息&#xff0c;使用pcl::PointXYZRGBA类型打开会提示以下信息&#xff1a; Failed to find match for field rgba另外&#xff0c;显示出来的点云是黑色&#xff0c;如果使用默认背景色为黑色&#xff0c;就无法显示点云了。 如果设置其它背景色&#xf…

视频监控平台:支持交通部行业标准JT/T905协议(即:出租汽车服务管理信息系统)的源代码的函数和功能介绍及分享

目录 一、视频监控平台介绍 &#xff08;一&#xff09;概述 &#xff08;二&#xff09;视频接入能力介绍 &#xff08;三&#xff09;功能介绍 二、JT/T905协议介绍 &#xff08;一&#xff09;概述 &#xff08;二&#xff09;主要内容 1、设备要求 2、业务功能要求…

[最全]设计模式实战(一)UML六大原则

UML类图 UML类图是学习设计模式的基础,学习设计模式,主要关注六种关系。即:继承、实现、组合、聚合、依赖和关联。 UML类图基本用法 继承关系用空心三角形+实线来表示。实现接口用空心三角形+虚线来表示。eg:大雁是最能飞的,它实现了飞翔接口。 关联关系用实线箭头来表示…

OOM日志分析

目录 1. 日志分析2. MAT 工具2.1 日志打印方式2.1.1 HeapDumpOnOutOfMemoryError&#xff08;推荐&#xff09;2.1.2 jmp 命令 2.2 MAT分析方式2.2.1 饼图分析2.2.2 树形图分析2.2.2 泄漏疑点 3. 优化 首先说一下结论&#xff1a;通过MAT工具分析才是最精准的&#xff0c;直接通…

2025秋招NLP算法面试真题(二)-史上最全Transformer面试题:灵魂20问帮你彻底搞定Transformer

简单介绍 之前的20个问题的文章在这里&#xff1a; https://zhuanlan.zhihu.com/p/148656446 其实这20个问题不是让大家背答案&#xff0c;而是为了帮助大家梳理 transformer的相关知识点&#xff0c;所以你注意看会发现我的问题也是有某种顺序的。 本文涉及到的代码可以在…

C语言中的字符输入/输出和验证输入

在C语言中&#xff0c;字符输入/输出功能允许程序与用户进行交互&#xff0c;读取用户的输入信息并展示输出结果。同时&#xff0c;验证输入的作用在于确保用户输入的数据符合预期&#xff0c;以提高程序的稳定性和可靠性&#xff0c;防止无效输入引发的错误或异常行为&#xf…

JavaWeb——MySQL:DDL操作库

目录 1.DDL&#xff1a;查询数据库&#xff1b; 1.1 查询数据库 1.2 创建数据库 1.DDL&#xff1a;查询数据库&#xff1b; 具体操作&#xff1a;增 删 查 用 &#xff1b; 1.1 查询数据库 SQL语句&#xff1a;show databases; 由于我创建过一些数据库&#xff0c;我查询的…

[Spring Boot]Netty-UDP客户端

文章目录 简述Netty-UDP集成pom引入ClientHandler调用 消息发送与接收在线UDP服务系统调用 简述 最近在一些场景中需要使用UDP客户端进行&#xff0c;所以开始集成新的东西。本文集成了一个基于netty的SpringBoot的简单的应用场景。 Netty-UDP集成 pom引入 <!-- netty --…