主线程 Looper.loop() 死循环为何不会ANR
先看下 ActivityThread 中的这段代码:
而 loop() 方法中,存在一个死循环:
public static void loop() {
...
...
...
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
...
...
...
}
}
如果 ActivityThread 中的 main() 方法没有 Looper.loop() 进行循环,那么 ActivityThread 运行完毕就会退出,相应的应用也就退出了。这就是这个死循环存在的必要性。
而ActivityThread 并不是一个Thread。而主线程就是从main 方法开始。在这个类中有个内部类 H,继承 Handler,内部有handleMessage 方法:
class H extends Handler {
...
...
...
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
case RELAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");
ActivityClientRecord r = (ActivityClientRecord)msg.obj;
handleRelaunchActivity(r);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
case PAUSE_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
SomeArgs args = (SomeArgs) msg.obj;
handlePauseActivity((IBinder) args.arg1, false,
(args.argi1 & USER_LEAVING) != 0, args.argi2,
(args.argi1 & DONT_REPORT) != 0, args.argi3);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
case PAUSE_ACTIVITY_FINISHING: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
SomeArgs args = (SomeArgs) msg.obj;
handlePauseActivity((IBinder) args.arg1, true, (args.argi1 & USER_LEAVING) != 0,
args.argi2, (args.argi1 & DONT_REPORT) != 0, args.argi3);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
...
...
}
...
...
...
}
在此方法中,有 handleLaunchActivity,handleRelaunchActivity,handlePauseActivity ... 等方法,是不是跟 Activity 的生命周期非常的相似呢?
选择其中一个 handlePauseActivity 方法来追:
private void handlePauseActivity(IBinder token, boolean finished,
boolean userLeaving, int configChanges, boolean dontReport, int seq) {
ActivityClientRecord r = mActivities.get(token);
if (DEBUG_ORDER) Slog.d(TAG, "handlePauseActivity " + r + ", seq: " + seq);
if (!checkAndUpdateLifecycleSeq(seq, r, "pauseActivity")) {
return;
}
if (r != null) {
//Slog.v(TAG, "userLeaving=" + userLeaving + " handling pause of " + r);
if (userLeaving) {
performUserLeavingActivity(r);
}
r.activity.mConfigChangeFlags |= configChanges;
performPauseActivity(token, finished, r.isPreHoneycomb(), "handlePauseActivity");
// Make sure any pending writes are now committed.
if (r.isPreHoneycomb()) {
QueuedWork.waitToFinish();
}
// Tell the activity manager we have paused.
if (!dontReport) {
try {
ActivityManager.getService().activityPaused(token);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
mSomeActivitiesChanged = true;
}
}
再看以下几个方法:
final Bundle performPauseActivity(IBinder token, boolean finished,
boolean saveState, String reason) {
ActivityClientRecord r = mActivities.get(token);
return r != null ? performPauseActivity(r, finished, saveState, reason) : null;
}
final Bundle performPauseActivity(ActivityClientRecord r, boolean finished,
boolean saveState, String reason) {
...
// Next have the activity save its current state and managed dialogs...
if (!r.activity.mFinished && saveState) {
callCallActivityOnSaveInstanceState(r);
}
performPauseActivityIfNeeded(r, reason);
...
}
private void performPauseActivityIfNeeded(ActivityClientRecord r, String reason) {
...
try {
r.activity.mCalled = false;
mInstrumentation.callActivityOnPause(r.activity);
...
}
从最后一个方法里面,调用了 mInstrumentation.callActivityOnPause(r.activity);
在类 Instrumentation 中的 callActivityOnPause 方法如下:
/**
* Perform calling of an activity's {@link Activity#onPause} method. The
* default implementation simply calls through to that method.
*
* @param activity The activity being paused.
*/
public void callActivityOnPause(Activity activity) {
activity.performPause();
}
Activity 类中的 performPause 方法如下:
final void performPause() {
mDoReportFullyDrawn = false;
mFragments.dispatchPause();
mCalled = false;
onPause();
writeEventLog(LOG_AM_ON_PAUSE_CALLED, "performPause");
mResumed = false;
if (!mCalled && getApplicationInfo().targetSdkVersion
>= android.os.Build.VERSION_CODES.GINGERBREAD) {
throw new SuperNotCalledException(
"Activity " + mComponent.toShortString() +
" did not call through to super.onPause()");
}
}
在这里看到了熟悉的生命周期中的方法 onPause() 。由此可以看出 Activity的生命周期不过就是Looper 传递消息中的某一个消息而已,所以Looper中的死循环不仅不会造成ANR,反而 Activity 的生命周期还要靠 Looper 来执行。
主线程 Looper.loop() 死循环为何不会ANR的更多相关文章
- 主线程中的Looper.loop()为什么不会造成ANR
引子: 正如我们所知,在android中如果主线程中进行耗时操作会引发ANR(Application Not Responding)异常. 造成ANR的原因一般有两种: 当前的事件没有机会得到处理(即 ...
- 关于Android中为什么主线程不会因为Looper.loop()里的死循环卡死?引发的思考,事实可能不是一个 epoll 那么 简单。
( 转载请务必标明出处:http://www.cnblogs.com/linguanh/, 本文出自:[林冠宏(指尖下的幽灵)的博客]) 前序 本文将会把一下三个问题阐述清楚以及一个网上的普遍观点的补 ...
- (转)关于Android中为什么主线程不会因为Looper.loop()里的死循环卡死?引发的思考,事实可能不是一个 epoll 那么 简单。
( 转载请务必标明出处:http://www.cnblogs.com/linguanh/, 本文出自:[林冠宏(指尖下的幽灵)的博客]) 前序 本文将会把一下三个问题阐述清楚以及一个网上的普遍观点的补 ...
- Looper.loop() android线程中的消息循环
Looper用于封装了android线程中的消息循环,默认情况下一个线程是不存在消息循环(message loop)的,需要调用Looper.prepare()来给线程创建一个消息循环,调用Loope ...
- Android 线程 Looper.prepare()、Looper.loop() 使用
优化项目过程中发现了一个非常Low的问题,整理一下.备忘: 说问题之前先看下HandlerThread的定义 一个封装了looper的线程: Looper用于封装了android线程中的消息循环. ...
- Looper.prepare()和Looper.loop()
什么时候需要 Looper Looper用于封装了android线程中的消息循环,默认情况下一个线程是不存在消息循环(message loop)的,需要调用Looper.prepare()来给线程创建 ...
- Android中为什么主线程不会因为Looper.loop()方法造成阻塞
很多人都对Handler的机制有所了解,如果不是很熟悉的可以看看我 如果看过源码的人都知道,在处理消息的时候使用了Looper.loop()方法,并且在该方法中进入了一个死循环,同时Looper.lo ...
- Android -- Looper.prepare()和Looper.loop() —深入版
Android中的Looper类,是用来封装消息循环和消息队列的一个类,用于在android线程中进行消息处理.handler其实可以看做是一个工具类,用来向消息队列中插入消息的. (1) Loope ...
- Android ActivityThread(主线程或UI线程)简介
1. ActivityThread功能 它管理应用进程的主线程的执行(相当于普通Java程序的main入口函数),并根据AMS的要求(通过IApplicationThread接口,AMS为Client ...
随机推荐
- Java并发编程——线程的基本概念和创建
一.线程的基本概念: 1.什么是进程.什么是是线程.多线程? 进程:一个正在运行的程序(程序进入内存运行就变成了一个进程).比如QQ程序就是一个进程. 线程:线程是进程中的一个执行单元,负责当前进程中 ...
- javascript中 visibility和display区别在哪
差异: 1.占用的空间不同. 可见性占用域空间,而显示不占用. 可见性和显示可以隐藏页面,例如: 将元素显示属性设置为block将在该元素后换行. 将元素显示属性设置为inline将消除元素换行. 将 ...
- 2019-11-29-解决从旧格式的-csproj-迁移到新格式的-csproj-格式-AssemblyInfo-文件值重复问题...
title author date CreateTime categories 解决从旧格式的 csproj 迁移到新格式的 csproj 格式 AssemblyInfo 文件值重复问题 lindex ...
- Delphi ActiveX编程
樊伟胜
- K8S搭建过程随笔_证书CFSSL
安装CFSSL mkdir -p /opt/k8s/cert && cd /opt/k8swget https://pkg.cfssl.org/R1.2/cfssl_linux-amd ...
- 海康RTSP取流URL格式
预览取流url [海康威视]举例说明: 主码流取流: rtsp://admin:12345@192.0.0.64:554/h264/ch1/main/av_stream 子码流取流: rtsp://a ...
- 小程序UI设计(10)-巧用模板,事半功倍
工具中为小程序员们准备了符合微信开发规范的模板.之前帖子中介绍的规范都在模板中已经设计好了,可以直接复制粘贴使用.下图中的样式是从模板直接复制过来的.实际使用时只要更换为自己的图片和文字即可.自动生成 ...
- Tomcat基础知识
介绍Tomcat之前先介绍下Java相关的知识. 各常见组件: 1.服务器(server):Tomcat的一个实例,通常一个JVM只能包含一个Tomcat实例:因此,一台物理服务器上可以在启动多个JV ...
- 理论基础+实战控制台程序实现AutoFac注入
[半小时大话.net依赖注入](一)理论基础+实战控制台程序实现AutoFac注入 系列目录# 第一章|理论基础+实战控制台程序实现AutoFac注入 第二章|AutoFac的常见使用套路 第三章 ...
- Ubuntu下python开发环境搭建
配置语言 1) 依次点击设置--Region & Language--Manage Installed Languages --install/remove language--chinese ...