应用卡死,也就是ANR所产生的原因?
1、5秒钟之内没有响应输入的事件,比如按键、屏幕触摸等。
2、广播接收器在10秒内没有执行完毕。
为什么说应用所有的操作都是在loop()中来管理?
首先,我们的每一个应用都存在于自己的虚拟机中,也就是说每一个应用都有自己的一个main函数,这个main函数就是ActivityThread.java的main()函数。
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
SamplingProfilerIntegration.start();
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
为什么每一个应用会有自己的一个main函数呢?
当我们在launcher界面启动一个应用的时候,这时候,系统就会用zygote给我们分配一个虚拟机,然后,这个应用就会运行在这个虚拟机上面。
应用运行到虚拟机之后,首先它要执行的就是启动ActivityThread,在ActivityThread中,它又会启动它的main()函数。
在main()函数中,它最重要的两行代码:
public static void main(String[] args) {
...
Looper.prepareMainLooper();
...
Looper.loop();
}
所以在程序运行的时候,主线程所有的代码都运行在这个Looper里面。
也就是说应用所有生命周期的函数(包括Activity、Service所有生命周期)都运行在这个Looper里面,而且,它们都是以消息的方式存在的。
假如说一个Activity启动,要走onResume()函数的时候,它就会在Activity的H里面执行RESUME_ACTIVITY。
case RESUME_ACTIVITY: return "RESUME_ACTIVITY";
它发送了一个Resume的消息,再接着看下这个Resume这个消息做了什么事情,代码在ActivityThread.java的handleMessage中。
case RESUME_ACTIVITY:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityResume");
SomeArgs args = (SomeArgs) msg.obj;
handleResumeActivity((IBinder) args.arg1, true, args.argi1 != 0, true,
args.argi3, "RESUME_ACTIVITY");
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
进入handleResumeActivity()方法。
// TODO Push resumeArgs into the activity for consideration
r = performResumeActivity(token, clearHide, reason);
它触发了Activity的管理机制,在Activity的管理机制里面,他就会发送一个消息,这个消息,就是对Activity进行Resume的操作。
r.activity.performResume();
接着往performResume()方法里面走,就进入了Activity.java。
final void performResume() {
....
mFragments.dispatchResume();
mFragments.execPendingActions();
....
}
再后面就调用了Fragemet的管理机制。
public void dispatchResume() {
mHost.mFragmentManager.dispatchResume();
}
因为类的继承,到最终是继承自了FragmentActivity,所以最后又变成了对Fragment的Resume。
既然Handler的消息全都是loop来的,为什么我们没有ANR问题?
其实产生ANR问题的不是Looper.loop(),哪怕主线程正在等待(block)。
for (;;) {
Message msg = queue.next(); // might block
...
}
因为这个时候(阻塞)的时候,说明主线程在休眠。
之前不是说5秒钟不相应就会出现阻塞问题吗,为什么休眠个好长时间也并不会被ANR呢?
了解这个问题,先看唤醒主线程的方式有哪些:
1、输入事件
主线程虽然被block了,但与ANR的问题是没有关系的,只要输入事件有响应,他会唤醒,就不会被block了。
所以,产生ANR的问题不是因为主线程睡眠了,而是因为输入事件没有响应,输入事件没有响应他就没有办法唤醒这个Looper,才加了这个5秒的限制。
2、往Looper里面添加消息的时候,它会唤醒这个Looper。
因为应用中不管是Activity,还是Service,所有的操作都是在各自的生命周期中执行的,所以它所有的操作都逃不出生命周期。所以,所有的操作都执行在ActivityThread.java中的loop()里面,所以,应用所有的操作都是在这个loop()中来管理的,也正是因为这个原因,主线程的loop()是不能够退出去的。
只有一种情况,我们在一个应用的一个界面下不动,这个应用没有任何事件发生,也没有任何别的事件要处理,这个时候,我们的Looper就处于一个block状态;当点击一下这个屏幕,他就会触发唤醒这个Looper。
总结
为什么没有导致应用卡死?
因为应用卡死压根与这个Looper没有关系,应用在没有消息需要处理的时候,它是在睡眠,释放线程;卡死是ANR,而Looper是睡眠。
卡死是在主线程中执行一个耗时的操作,loop()会一直在处理一个消息,而for循环中有很多消息需要被处理,而这一个消息就要处理很久,这一个消息的处理时间,会转变成其他的点击事件没有响应。
因为主线程在接受到其他消息的时候没有时间去响应,它的时间都在处理那一个耗时的操作,造成点击事件没有办法响应,点击事件没有办法响应就容易出现ANR。
原文作者:Seas.Su
原文链接:https://blog.csdn.net/yichen97/java/article/details/106367067
网友评论