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

news2024/11/27 6:36:10

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.coloradmin.cn/o/1851894.html

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

相关文章

Spring Boot启动报错Lombok supports: sun/apple javac 1.6, ECJ

版本 idea 2023.3.4 <dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.32</version></dependency> 解决方式 File->Settings->Build, Execution, Deployment->Com…

秋招突击——6/22——复习{区间DP——加分二叉树,背包问题——买书}——新作{溢出元素、实现strStr()}

文章目录 引言复习区间DP——加分二叉树个人实现 背包问题——买书个人实现参考实现 新作移除元素个人实现参考思路 找出字符串中第一个匹配项的下标个人实现参考实现 总结 引言 今天做了一个噩梦&#xff0c;然后流了一身汗&#xff0c;然后没起来&#xff0c;九点多才起床背…

Android中屏蔽 电源键长按、Home键、Home长按

“电源键长按”&#xff08;globalscreen&#xff09; “Home键”&#xff08;homekey&#xff09; “Home长按”&#xff08;recentapps&#xff09; 我们可以使用广播来实现&#xff0c;如&#xff1a; [java] view plain copy print ? package com.jumpinus.test; im…

系统架构师概述

引言 系统架构设计师是项目开发活动中的众多角色之一&#xff0c;它可以是一个小组或者一个人或者是一个团队&#xff0c;架构师包含建筑师&#xff0c;设计师&#xff0c;创造者&#xff0c;缔造者&#xff0c;可以说架构师就是我们社会各个领域的创造者和缔造者。从组织上划分…

基于simulink的PEM燃料电池控制系统建模与仿真,对比PID,积分分离以及滑模控制器

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 4.1 PID控制器 4.2 积分分离PID控制器 4.3 滑模控制器 5.完整工程文件 1.课题概述 基于simulink的PEM燃料电池控制系统建模与仿真,对比PID,积分分离以及滑模控制器。 2.系统仿真结果 &#xff08;完…

华为---OSPF被动接口配置(四)

9.4 OSPF被动接口配置 9.4.1 原理概述 OSPF被动接口也称抑制接口&#xff0c;成为被动接口后&#xff0c;将不会接收和发送OSPF报文。如果要使OSPF路由信息不被某一网络中的路由器获得且使本地路由器不接收网络中其他路由器发布的路由更新信息&#xff0c;即已运行在OSPF协议…

如何在 Ubuntu 12.04 VPS 上安装和配置基本的 LDAP 服务器

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 简介 LDAP&#xff08;轻量级目录访问协议&#xff09;是一种通过文件和目录层次结构管理相关信息的协议&#xff0c;它可以从集中位置管…

【嵌入式Linux】i.MX6ULL 时钟树——理论分析

文章目录 0. 时钟树结构0.1 参考手册 Chapter 18​: Clock Controller Module (CCM)0.2 时钟信号路径 1. 时钟源——晶振1.1 外部低频时钟 - CKIL1.1.1 CKIL 同步到 IPG_CLK 解释 1.2 外部高频时钟 - CKIH 和 内部振荡器1.3 总结1.4 缩写补充 2. PLL时钟2.1 i.MX6U 芯片 PLL 时…

Android Studio 安卓手机上实现火柴人动画(Java源代码—Python)

android:layout_marginLeft“88dp” android:layout_marginTop“244dp” android:text“Python” android:textSize“25sp” app:layout_constraintStart_toStartOf“parent” app:layout_constraintTop_toTopOf“parent” /> </androidx.constraintlayout.widget.…

基于顺序存储的环形队列算法库构建

学习贺利坚老师基于数组的环形队列 数据结构之自建算法库——顺序环形队列_下空队列q中依次入队列数据元素abc-CSDN博客文章浏览阅读5.2k次&#xff0c;点赞6次&#xff0c;收藏6次。本文针对数据结构基础系列网络课程(3)&#xff1a;栈和队列中第9课时环形队列的存储及基本操…

用Visual Studio调试CMake项目并生成Visual Studio工程

一. 在Windows系统上安装CMake 访问CMake官方网站https://cmake.org/download&#xff0c;或通过文末链接下载&#xff1a;在下载页面上&#xff0c;找到并点击“Download”链接以获取最新的稳定版本的CMake。请注意&#xff0c;虽然新版本可能包含更多功能和改进&#xff0c;…

MATLAB | 怎样绘制这样的环形柱状图

Hey, 各位又是好久不见&#xff0c;最近忙到后台消息都有些来不及看&#xff0c;非常抱歉&#xff0c;今天带来一个环形柱状图绘制的简易小代码,绘制效果如下&#xff1a; 下面直接给出完整代码&#xff0c;替换一下数据即可&#xff0c;代码都有注释的&#xff1a; 完整代码 …

Redis—SortedSet数据类型及其常用命令详解

文章目录 一、Redis概述SortedSet类型1 ZADD&#xff1a;向有序集合添加成员2 ZREM&#xff1a;从有序集合中移除一个或多个成员3 ZSCORE : 获取sorted set中的指定元素的score值4 ZRANK&#xff1a;获取sorted set 中的指定元素的排名5 ZCARD&#xff1a;获取sorted set中的元…

库卡机器人减速机维修齿轮磨损故障

一、KUKA机器人减速器齿轮磨损故障的原因 1. 润滑不足&#xff1a;润滑油不足或质量不佳可能导致齿轮磨损。 2. 负载过重&#xff1a;超过库卡机械臂减速器额定负载可能导致齿轮磨损。 3. 操作不当&#xff1a;未按照说明书操作可能导致KUKA机器人减速器齿轮磨损。 4. 维护不足…

Unity Apple Vision Pro 开发(三):visionOS 应用形态

文章目录 &#x1f4d5;教程说明&#x1f4d5;常用名词解释&#x1f4d5;visionOS 空间类型⭐Shared Space 共享空间⭐Full Space/Immersive Space 独占空间 &#x1f4d5;visionOS 渲染框架&#x1f4d5;Unity 开发 visionOS 应用的不同模式⭐**窗口模式**⭐VR 模式⭐MR 模式 …

关于edge浏览器注册Kaggle不显示验证部分的问题

使用edge注册kaggle没有显示验证的部分导致无法完成注册 法一 谷歌大法好&#xff0c;使用谷歌注册就么有问题&#xff0c;然鹅需要魔法上网。 法二 使用 edge的Header Editor的插件 收到邮件后填写即可 参考博客&#xff1a; Kaggle平台注册弹不出验证码怎么办&#…

【C++LeetCode】【热题100】最长连续序列【中等】-不同效率的题解【5】

题目&#xff1a; 暴力方法&#xff1a; class Solution { public:int longestConsecutive(vector<int>& nums) {int maxlen1;//定义最长连续序列if(nums.size()<1){//特殊情况的长度 等于序列长度return nums.size();}std::unordered_set<int> uniqu…

内容安全复习 8 - 视觉内容伪造与检测

文章目录 研究背景内容伪造方法虚假人脸生成人脸替换属性编辑表情重演跨模态人脸编辑 伪造检测方法眨眼检测交互式人脸活体检测一些了解方法挑战 研究背景 图像内容篡改造成新闻报道的偏颇易导致社会和公共秩序的不安&#xff0c;对公共安全产生不良影响。 造成的影响&#x…

数据结构经典面试之列表——C#和C++篇

文章目录 1. 数据结构概述2. 列表&#xff08;List&#xff09;的基本概念与操作3. 列表的具体实现方式3.1 数组实现3.2 链表实现 4. 列表在C#和C中的使用示例4.1 C#中的列表使用示例4.2 C中的列表使用示例 5. 总结 数据结构是计算机科学中非常重要的一个领域&#xff0c;它主要…