登录

去注册

登录

注册

去登录

注册

每日一问 ANR的产生的原理是什么,AMS中涉及ANR的代码有哪些?

xiaoyang   2019-06-30   收藏

可以仔细思考下ANR是如何产生的?


我在 UI 线程执行一个非常耗时的操作一定会出现 ANR 弹框吗?


本周 3/3 

17

源码分析:

打开ActivityManagerService的源码(在AS中先进入ActivityManager,再查找引用ActivityManager的类即能进入AMS),
在1891行(我使用的sdkVersion是28)可以看到:

static final int SHOW_NOT_RESPONDING_UI_MSG = 2;


看名字能大概猜到,这就是弹出ANR对话框的message标识了,ctrl点下它,可以看到这个:

case SHOW_NOT_RESPONDING_UI_MSG: {
mAppErrors.handleShowAnrUi(msg);
}


可以看到它里面会调用AppErrors的handleShowAnrUi方法,
这个方法里面它会创建一个AppNotRespondingDialog(系统的自定义Dialog),最终会以TYPE_SYSTEM_ERROR的方式弹出。

到这里基本可以确定,弹出ANR对话框的条件就是AMS中的UiHandler收到了what为SHOW_NOT_RESPONDING_UI_MSG的消息,


那么这个消息是在哪里发出的呢?

ctrl点开1891行SHOW_NOT_RESPONDING_UI_MSG,会看到在AppErrors类中的appNotResponding方法里有引用:

// Bring up the infamous App Not Responding dialog
Message msg = Message.obtain();
msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
msg.obj = new AppNotRespondingDialog.Data(app, activity, aboveSystem);
mService.mUiHandler.sendMessage(msg);

可以看到,在这个方法里会给AMS(那个mService就是AMS的实例)的UiHandler发送请求弹出ANR对话框,那到底在哪些地方调用这个方法的呢?

一共有4处,分别是:

  1. ActiveServices的serviceTimeout方法;
  2. ActiveServices的serviceForegroundTimeout方法;
  3. ActivityManagerService的appNotRespondingViaProvider方法;
  4. ActivityManagerService的inputDispatchingTimedOut方法;

看方法名字:

  • 第一个,很明显就是后台服务超时发出的;
  • 第二个,serviceForeground,应该就是前台服务超时发出的了;
  • 第三个,appNotRespondingViaProvider,通过Provider发出的无响应,看里面的代码会看到"ContentProvider not responding",这个ANR也就是由ContentProvider造成的了。
  • 最后一个,inputDispatchingTimedOut,就是input事件分派的时候超时(处理事件时被阻塞)所发出的,input事件,我们知道分两种,一种是KeyEvent(按键),另一种是MotionEvent(触摸)。

UI线程执行一个非常耗时的操作一定会出现ANR弹框吗?

我们来看刚刚的inputDispatchingTimedOut方法的代码:

public boolean inputDispatchingTimedOut(final ProcessRecord proc,
final ActivityRecord activity, final ActivityRecord parent,
final boolean aboveSystem, String reason) {
if (没过滤事件权限的) {
抛出安全异常,提示无权限
}

if (reason参数 != null){
等下弹ANR框时加上这个reason
}

if (proc.debugging) {
return false;
}

if (proc.instr != null) {
Bundle info = new Bundle();
info.putString("shortMsg", "keyDispatchingTimedOut");
info.putString("longMsg", annotation);
finishInstrumentationLocked(proc, Activity.RESULT_CANCELED, info);
return true;
}
mAppErrors.appNotResponding(proc, activity, parent, aboveSystem, annotation);
return true;
}


可以看到,调用appNotResponding方法(弹框)的,是在倒数第二行,

但是!我们发现了有两种情况,是会直接return而不往下执行的(不弹框):

  1. proc.debugging=true的时候,点进去看注释,会看到这句:"was app launched for debugging?",也就是在调试的时候,尽管处理输入事件超时,也不会弹出ANR框(这也很合理,我们经常会在断点上停留一段时间分析数据,这时候如果频繁弹框的话,就会显得很不人性化);
  2. proc.instr不为空的时候,这个情况就比较暴力了,会直接kill掉进程;

于是,有同学可能会使出一招"夺命连环问":

那什么情况下这个instr不为空呢(ANR时会直接被kill掉)?

它是当AMS的attachApplication方法被调用时,有可能被赋值。

什么时候attachApplication会被调用呢?

其实就是ActivityThread的main方法执行的时候(启动),它会调用一个attach方法,而attachApplication会在这个attach方法里面被调用。

赋值条件是什么?

赋值条件是mActiveInstrumentation里面不为空。

那么什么时候mActiveInstrumentation里面不为空?

看AMS代码7763行(检查mActiveInstrumentation是否为空那里)可以发现有一句注释:"Check if this is a secondary process",
那我们可以知道,这个判断是检测当前进程是否次进程(多进程环境下),如果是次线程(不是主进程),那么经过安全检查之后,就会把mActiveInstrumentation里面的实例,赋值给proc的instr。

总结:

ANR对话框,是在AMS收到SHOW_NOT_RESPONDING_UI_MSG消息之后弹出的。这个消息分别来自于:
后台服务超时;
前台服务超时;
ContentProvider超时;
input事件分派超时;

在input事件分派超时的时候,有两种情况不会弹框,分别是:
处于debug时;
来自子进程;(这个情况下会直接kill掉子进程)

回复
sunchaoyang : @Alex_Lin 

我的也是28,怎么没看到后面那些代码

2019-09-02 回复
1668904200@qq.com : @陈小缘 

暂,太喜欢这种叙述方式了

2019-07-11 回复
willwaywang6 : @陈小缘 

点赞!!!

2019-07-02 回复
Alex_Lin : @陈小缘 

真详细,点个赞

2019-06-28 回复
2

ANR:Application Not Responding,即应用无响应

ANR类型:

    1. KeyDispatchTimeout(5 seconds) --主要类型按键或触摸事件在特定时间内无响应

    2. BroadcastTimeout(10 seconds) --BroadcastReceiver在特定时间内无法处理完成

    3. ServiceTimeout(20 seconds) --小概率类型 Service在特定的时间内无法处理完成

其中,A key or touch event was not dispatched within the specified time(按键或触摸事件在特定时间内无响应)

具体的超时时间的定义在framework下的ActivityManagerService.java

//How long we wait until we timeout on key dispatching.

staticfinal int KEY_DISPATCHING_TIMEOUT = 5*1000

所以在UI线程中如果使用一个非常耗时的操作(>5S),是否会ANR应该取决于在这个过程中是否有收到 touch event ,如果有,那必然会产生ANR 弹窗,如果没有,理论上不会。

    


回复
鸿洋 : @Alex_Lin 

2019-06-30 回复
1

AppErrors.appNotResponding();该方法是最终弹出ANR对话框的唯一入口,调用该方法的场景才会有ANR提示,也可以认为在主线程中执行无论再耗时的任务,只要最终不调用该方法,都不会有ANR提示,也不会有ANR相关日志及报告;通过调用关系可以看出哪些场景会导致ANR,有以下四种场景:
(1)Service Timeout:Service在特定的时间内无法处理完成;
(2)BroadcastQueue Timeout:BroadcastReceiver在特定时间内无法处理完成
(3)ContentProvider Timeout:内容提供者执行超时
(4)inputDispatching Timeout: 按键或触摸事件在特定时间内无响应。

更多内容你请参考:https://www.jianshu.com/p/ad1a84b6ec69

回复

删除留言

确认删除留言,会导致相关评论丢失?

取消 确定