转自:https://www.zhihu.com/question/34652589
举报
1 条评论
•
邀请回答
分享
按投票排序
按时间排序
26 个回答
要完全彻底理解这个问题,需要准备以下4方面的知识:Process/Thread,Android Binder IPC,Handler/Looper/MessageQueue消息机制,Linux pipe/epoll机制。
总结一下楼主主要有3个疑惑:
1.Android中为什么主线程不会因为Looper.loop()里的死循环卡死?
2.没看见哪里有相关代码为这个死循环准备了一个新线程去运转?
3.Activity的生命周期这些方法这些都是在主线程里执行的吧,那这些生命周期方法是怎么实现在死循环体外能够执行起来的?
--------------------------------------------------------------------------------------------------------------------------------------
针对这些疑惑, @hi大头鬼hi @Rocko @陈昱全 大家回答都比较精炼,接下来我再更进一步详细地一一解答楼主的疑惑:
(1) Android中为什么主线程不会因为Looper.loop()里的死循环卡死?
这里涉及线程,先说说说进程/线程, 进程:每个app运行时前首先创建一个进程,该进程是由Zygote fork出来的,用于承载App上运行的各种Activity/Service等组件。进程对于上层应用来说是完全透明的,这也是google有意为之,让App程序都是运行在Android Runtime。大多数情况一个App就运行在一个进程中,除非在AndroidManifest.xml中配置Android:process属性,或通过native代码fork进程。
线程:线程对应用来说非常常见,比如每次new Thread().start都会创建一个新的线程。该线程与App所在进程之间资源共享,从Linux角度来说进程与线程除了是否共享资源外,并没有本质的区别,都是一个task_struct结构体 ,在CPU看来进程或线程无非就是一段可执行的代码,CPU采用CFS调度算法,保证每个task都尽可能公平的享有CPU时间片。
有了这么准备,再说说死循环问题:
对于线程既然是一段可执行的代码,当可执行代码执行完成后,线程生命周期便该终止了,线程退出。而对于主线程,我们是绝不希望会被运行一段时间,自己就退出,那么如何保证能一直存活呢? 简单做法就是可执行代码是能一直执行下去的,死循环便能保证不会被退出,例如,binder线程也是采用死循环的方法,通过循环方式不同与Binder驱动进行读写操作,当然并非简单地死循环,无消息时会休眠。但这里可能又引发了另一个问题,既然是死循环又如何去处理其他事务呢?通过创建新线程的方式。
真正会卡死主线程的操作是在回调方法onCreate/onStart/onResume等操作时间过长,会导致掉帧,甚至发生ANR,looper.loop本身不会导致应用卡死。
(2) 没看见哪里有相关代码为这个死循环准备了一个新线程去运转?
事实上,会在进入死循环之前便创建了新binder线程,在代码ActivityThread.main()中:
thread.attach(false);便会创建一个Binder线程(具体是指ApplicationThread,Binder的服务端,用于接收系统服务AMS发送来的事件),该Binder线程通过Handler将Message发送给主线程,具体过程可查看
startService流程分析,这里不展开说,简单说Binder用于进程间通信,采用C/S架构。关于binder感兴趣的朋友,可查看我回答的另一个知乎问题:
为什么Android要采用Binder作为IPC机制? - Gityuan的回答
另外, ActivityThread实际上并非线程,不像HandlerThread类,ActivityThread并没有真正继承Thread类,只是往往运行在主线程,该人以线程的感觉,其实承载ActivityThread的主线程就是由Zygote fork而创建的进程。
主线程的死循环一直运行是不是特别消耗CPU资源呢? 其实不然,这里就涉及到 Linux pipe/epoll机制,简单说就是在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,详情见 Android消息机制1-Handler(Java层),此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。 所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。
(3) Activity的生命周期是怎么实现在死循环体外能够执行起来的?
ActivityThread的内部类H继承于Handler,通过handler消息机制,简单说Handler机制用于同一个进程的线程间通信。
Activity的生命周期都是依靠主线程的Looper.loop,当收到不同Message时则采用相应措施:
在H.handleMessage(msg)方法中,根据接收到不同的msg,执行相应的生命周期。
比如收到msg=H.LAUNCH_ACTIVITY,则调用ActivityThread.handleLaunchActivity()方法,最终会通过反射机制,创建Activity实例,然后再执行Activity.onCreate()等方法;
再比如收到msg=H.PAUSE_ACTIVITY,则调用ActivityThread.handlePauseActivity()方法,最终会执行Activity.onPause()等方法。 上述过程,我只挑核心逻辑讲,真正该过程远比这复杂。
主线程的消息又是哪来的呢?当然是App进程中的其他线程通过Handler发送给主线程,请看接下来的内容:
--------------------------------------------------------------------------------------------------------------------------------------
最后,从进程与线程间通信的角度,通过一张图加深大家对App运行过程的理解:
system_server进程是系统进程,java framework框架的核心载体,里面运行了大量的系统服务,比如这里提供ApplicationThreadProxy(简称ATP),ActivityManagerService(简称AMS),这个两个服务都运行在system_server进程的不同线程中,由于ATP和AMS都是基于IBinder接口,都是binder线程,binder线程的创建与销毁都是由binder驱动来决定的。
App进程则是我们常说的应用程序,主线程主要负责Activity/Service等组件的生命周期以及UI相关操作都运行在这个线程; 另外,每个App进程中至少会有两个binder线程 ApplicationThread(简称AT)和ActivityManagerProxy(简称AMP),除了图中画的线程,其中还有很多线程,比如signal catcher线程等,这里就不一一列举。
Binder用于不同进程之间通信,由一个进程的Binder客户端向另一个进程的服务端发送事务,比如图中线程2向线程4发送事务;而handler用于同一个进程中不同线程的通信,比如图中线程4向主线程发送消息。
结合图说说Activity生命周期,比如暂停Activity,流程如下:
--------------------------------------------------------------------------------------------------------------------------------------
如果大家觉得我回答得还行,还请大家随手 点赞、关注、收藏,如果觉得说得不好的,还往评论指正。
==========> 我的微博:Gityuan
==========> 我微信公众号: Gityuan
总结一下楼主主要有3个疑惑:
1.Android中为什么主线程不会因为Looper.loop()里的死循环卡死?
2.没看见哪里有相关代码为这个死循环准备了一个新线程去运转?
3.Activity的生命周期这些方法这些都是在主线程里执行的吧,那这些生命周期方法是怎么实现在死循环体外能够执行起来的?
--------------------------------------------------------------------------------------------------------------------------------------
针对这些疑惑, @hi大头鬼hi @Rocko @陈昱全 大家回答都比较精炼,接下来我再更进一步详细地一一解答楼主的疑惑:
(1) Android中为什么主线程不会因为Looper.loop()里的死循环卡死?
这里涉及线程,先说说说进程/线程, 进程:每个app运行时前首先创建一个进程,该进程是由Zygote fork出来的,用于承载App上运行的各种Activity/Service等组件。进程对于上层应用来说是完全透明的,这也是google有意为之,让App程序都是运行在Android Runtime。大多数情况一个App就运行在一个进程中,除非在AndroidManifest.xml中配置Android:process属性,或通过native代码fork进程。
线程:线程对应用来说非常常见,比如每次new Thread().start都会创建一个新的线程。该线程与App所在进程之间资源共享,从Linux角度来说进程与线程除了是否共享资源外,并没有本质的区别,都是一个task_struct结构体 ,在CPU看来进程或线程无非就是一段可执行的代码,CPU采用CFS调度算法,保证每个task都尽可能公平的享有CPU时间片。
有了这么准备,再说说死循环问题:
对于线程既然是一段可执行的代码,当可执行代码执行完成后,线程生命周期便该终止了,线程退出。而对于主线程,我们是绝不希望会被运行一段时间,自己就退出,那么如何保证能一直存活呢? 简单做法就是可执行代码是能一直执行下去的,死循环便能保证不会被退出,例如,binder线程也是采用死循环的方法,通过循环方式不同与Binder驱动进行读写操作,当然并非简单地死循环,无消息时会休眠。但这里可能又引发了另一个问题,既然是死循环又如何去处理其他事务呢?通过创建新线程的方式。
真正会卡死主线程的操作是在回调方法onCreate/onStart/onResume等操作时间过长,会导致掉帧,甚至发生ANR,looper.loop本身不会导致应用卡死。
(2) 没看见哪里有相关代码为这个死循环准备了一个新线程去运转?
事实上,会在进入死循环之前便创建了新binder线程,在代码ActivityThread.main()中:
public static void main(String[] args) {
....
//创建Looper和MessageQueue对象,用于处理主线程的消息
Looper.prepareMainLooper();
//创建ActivityThread对象
ActivityThread thread = new ActivityThread();
//建立Binder通道 (创建新线程)
thread.attach(false);
Looper.loop(); //消息循环运行
throw new RuntimeException("Main thread loop unexpectedly exited");
}
为什么Android要采用Binder作为IPC机制? - Gityuan的回答
另外, ActivityThread实际上并非线程,不像HandlerThread类,ActivityThread并没有真正继承Thread类,只是往往运行在主线程,该人以线程的感觉,其实承载ActivityThread的主线程就是由Zygote fork而创建的进程。
主线程的死循环一直运行是不是特别消耗CPU资源呢? 其实不然,这里就涉及到 Linux pipe/epoll机制,简单说就是在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,详情见 Android消息机制1-Handler(Java层),此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。 所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。
(3) Activity的生命周期是怎么实现在死循环体外能够执行起来的?
ActivityThread的内部类H继承于Handler,通过handler消息机制,简单说Handler机制用于同一个进程的线程间通信。
Activity的生命周期都是依靠主线程的Looper.loop,当收到不同Message时则采用相应措施:
在H.handleMessage(msg)方法中,根据接收到不同的msg,执行相应的生命周期。
比如收到msg=H.LAUNCH_ACTIVITY,则调用ActivityThread.handleLaunchActivity()方法,最终会通过反射机制,创建Activity实例,然后再执行Activity.onCreate()等方法;
再比如收到msg=H.PAUSE_ACTIVITY,则调用ActivityThread.handlePauseActivity()方法,最终会执行Activity.onPause()等方法。 上述过程,我只挑核心逻辑讲,真正该过程远比这复杂。
主线程的消息又是哪来的呢?当然是App进程中的其他线程通过Handler发送给主线程,请看接下来的内容:
--------------------------------------------------------------------------------------------------------------------------------------
最后,从进程与线程间通信的角度,通过一张图加深大家对App运行过程的理解:

system_server进程是系统进程,java framework框架的核心载体,里面运行了大量的系统服务,比如这里提供ApplicationThreadProxy(简称ATP),ActivityManagerService(简称AMS),这个两个服务都运行在system_server进程的不同线程中,由于ATP和AMS都是基于IBinder接口,都是binder线程,binder线程的创建与销毁都是由binder驱动来决定的。
App进程则是我们常说的应用程序,主线程主要负责Activity/Service等组件的生命周期以及UI相关操作都运行在这个线程; 另外,每个App进程中至少会有两个binder线程 ApplicationThread(简称AT)和ActivityManagerProxy(简称AMP),除了图中画的线程,其中还有很多线程,比如signal catcher线程等,这里就不一一列举。
Binder用于不同进程之间通信,由一个进程的Binder客户端向另一个进程的服务端发送事务,比如图中线程2向线程4发送事务;而handler用于同一个进程中不同线程的通信,比如图中线程4向主线程发送消息。
结合图说说Activity生命周期,比如暂停Activity,流程如下:
- 线程1的AMS中调用线程2的ATP;(由于同一个进程的线程间资源共享,可以相互直接调用,但需要注意多线程并发问题)
- 线程2通过binder传输到App进程的线程4;
- 线程4通过handler消息机制,将暂停Activity的消息发送给主线程;
- 主线程在looper.loop()中循环遍历消息,当收到暂停Activity的消息时,便将消息分发给ActivityThread.H.handleMessage()方法,再经过方法的调用,最后便会调用到Activity.onPause(),当onPause()处理完后,继续循环loop下去。
--------------------------------------------------------------------------------------------------------------------------------------
如果大家觉得我回答得还行,还请大家随手 点赞、关注、收藏,如果觉得说得不好的,还往评论指正。
==========> 我的微博:Gityuan
==========> 我微信公众号: Gityuan
Handler 机制应该基本都知道了,题主点进去源码看下 ActivityThread 就能基本搞明白了,为什么主线程不会因为 Looper.loop() 里的死循环卡死?
首先 ActivityThread 并不是一个 Thread,就只是一个 final 类而已。我们常说的主线程就是从这个类的 main 方法开始,main 方法很简短,一眼就能看全,我们看到里面有 Looper 了,那么接下来就 找找 ActivityThread 对应的 Handler 啊,就是内部类 H,其继承 Handler,贴出 handleMessage 的小部分:
看完这 Handler 里处理消息的内容应该明白了吧, Activity 的生命周期都有对应的 case 条件了,ActivityThread 有个 getHandler 方法,得到这个 handler 就可以发送消息,然后 loop 里就分发消息,然后就发给 handler, 然后就执行到 H(Handler )里的对应代码。所以这些代码就不会卡死~,有消息过来就能执行。举个例子,在 ActivityThread 里的内部类 ApplicationThread 中就有很多 sendMessage 的方法:
首先 ActivityThread 并不是一个 Thread,就只是一个 final 类而已。我们常说的主线程就是从这个类的 main 方法开始,main 方法很简短,一眼就能看全,我们看到里面有 Looper 了,那么接下来就 找找 ActivityThread 对应的 Handler 啊,就是内部类 H,其继承 Handler,贴出 handleMessage 的小部分:
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);
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");
handlePauseActivity((IBinder)msg.obj, false, (msg.arg1&1) != 0, msg.arg2,
(msg.arg1&2) != 0);
maybeSnapshot();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case PAUSE_ACTIVITY_FINISHING:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
handlePauseActivity((IBinder)msg.obj, true, (msg.arg1&1) != 0, msg.arg2,
(msg.arg1&1) != 0);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case STOP_ACTIVITY_SHOW:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStop");
handleStopActivity((IBinder)msg.obj, true, msg.arg2);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case STOP_ACTIVITY_HIDE:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStop");
handleStopActivity((IBinder)msg.obj, false, msg.arg2);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case SHOW_WINDOW:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityShowWindow");
handleWindowVisibility((IBinder)msg.obj, true);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case HIDE_WINDOW:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityHideWindow");
handleWindowVisibility((IBinder)msg.obj, false);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case RESUME_ACTIVITY:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityResume");
handleResumeActivity((IBinder) msg.obj, true, msg.arg1 != 0, true);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case SEND_RESULT:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityDeliverResult");
handleSendResult((ResultData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
...........
}
看完这 Handler 里处理消息的内容应该明白了吧, Activity 的生命周期都有对应的 case 条件了,ActivityThread 有个 getHandler 方法,得到这个 handler 就可以发送消息,然后 loop 里就分发消息,然后就发给 handler, 然后就执行到 H(Handler )里的对应代码。所以这些代码就不会卡死~,有消息过来就能执行。举个例子,在 ActivityThread 里的内部类 ApplicationThread 中就有很多 sendMessage 的方法:
......
public final void schedulePauseActivity(IBinder token, boolean finished,
boolean userLeaving, int configChanges, boolean dontReport) {
sendMessage(
finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY,
token,
(userLeaving ? 1 : 0) | (dontReport ? 2 : 0),
configChanges);
}
public final void scheduleStopActivity(IBinder token, boolean showWindow,
int configChanges) {
sendMessage(
showWindow ? H.STOP_ACTIVITY_SHOW : H.STOP_ACTIVITY_HIDE,
token, 0, configChanges);
}
public final void scheduleWindowVisibility(IBinder token, boolean showWindow) {
sendMessage(
showWindow ? H.SHOW_WINDOW : H.HIDE_WINDOW,
token);
}
public final void scheduleSleeping(IBinder token, boolean sleeping) {
sendMessage(H.SLEEPING, token, sleeping ? 1 : 0);
}
public final void scheduleResumeActivity(IBinder token, int processState,
boolean isForward, Bundle resumeArgs) {
updateProcessState(processState, false);
sendMessage(H.RESUME_ACTIVITY, token, isForward ? 1 : 0);
}
......
去看下handler机制就明白了,网上一大把。
1.handler机制是使用pipe来实现的
2.主线程没有消息处理时阻塞在管道的读端
3.binder线程会往主线程消息队列里添加消息,然后往管道写端写一个字节,这样就能唤醒主线程从管道读端返回,也就是说queue.next()会调用返回
4.dispatchMessage()中调用onCreate, onResume
1.handler机制是使用pipe来实现的
2.主线程没有消息处理时阻塞在管道的读端
3.binder线程会往主线程消息队列里添加消息,然后往管道写端写一个字节,这样就能唤醒主线程从管道读端返回,也就是说queue.next()会调用返回
4.dispatchMessage()中调用onCreate, onResume
ActivityThread是应用程序的入口,这里你可以看到写Java程序时司空见惯的main方法,而main方法正是整个Java程序的入口。
ActivityThread的main方法主要就是做消息循环,一旦退出消息循环,那么你的程序也就可以退出了。
从消息队列中取消息可能会阻塞,取到消息会做出相应的处理。如果某个消息处理时间过长,就可能会影响UI线程的刷新速率,造成卡顿的现象。
ActivityThread的main方法主要就是做消息循环,一旦退出消息循环,那么你的程序也就可以退出了。
从消息队列中取消息可能会阻塞,取到消息会做出相应的处理。如果某个消息处理时间过长,就可能会影响UI线程的刷新速率,造成卡顿的现象。
谁和你说没卡死,实际上就是卡死了。不信你new一个thread,在里面建立一个looper,然后looper.prepare,随后再打个log,你就会发现log死活不会打。
一个looper维护一个消息队列,队列里有消息就取出来执行,没消息就休眠直到有消息进入,就是个典型的生产者消费者模型。实际的消息模型会更复杂一些,定时执行和延迟执行关系到系统中断。
所以在looper启动后,主线程上执行的任何代码都是在一个被looper从消息队列里取出来执行的runnable内被执行而已,主线程此时就是被阻塞在一个无限循环里了。
activity life cycle callback也是如此,生命周期的回调首先是ams通过binder发送ipc调用给app进程,app进程里的binder stub接收到调用后,给main looper插了条runnable而已(通过和main looper绑定的handler完成)。
一个looper维护一个消息队列,队列里有消息就取出来执行,没消息就休眠直到有消息进入,就是个典型的生产者消费者模型。实际的消息模型会更复杂一些,定时执行和延迟执行关系到系统中断。
所以在looper启动后,主线程上执行的任何代码都是在一个被looper从消息队列里取出来执行的runnable内被执行而已,主线程此时就是被阻塞在一个无限循环里了。
activity life cycle callback也是如此,生命周期的回调首先是ams通过binder发送ipc调用给app进程,app进程里的binder stub接收到调用后,给main looper插了条runnable而已(通过和main looper绑定的handler完成)。
了解下linux的epoll你就知道为什么不会被卡住了,先说结论:阻塞是有的,但是不会卡住
主要原因有2个
1,epoll模型
当没有消息的时候会epoll.wait,等待句柄写的时候再唤醒,这个时候其实是阻塞的。
2,所有的ui操作都通过handler来发消息操作。
比如屏幕刷新16ms一个消息,你的各种点击事件,所以就会有句柄写操作,唤醒上文的wait操作,所以不会被卡死了。
主要原因有2个
1,epoll模型
当没有消息的时候会epoll.wait,等待句柄写的时候再唤醒,这个时候其实是阻塞的。
2,所有的ui操作都通过handler来发消息操作。
比如屏幕刷新16ms一个消息,你的各种点击事件,所以就会有句柄写操作,唤醒上文的wait操作,所以不会被卡死了。
这里是 6.0 的 ActivityThread main 方法的源代码:
Looper.loop() 确实是一个死循环,但是 Looper.loop() 后面就没有东西了啊(轮到后面那一句执行就是程序异常退出了)。
Looper.loop() 就这样一直在主线程守着,我们的 App 才有机会等待用户来操作,才不会执行完 main() 方法就结束了。
Looper.loop() 这个方法在主线程循环着,当有消息的时候就进行消息处理,可以调用我们在 Activity 里写的生命周期方法,也就说我们的代码其实就是在这个循环里面去执行的,当然不会阻塞了。
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
Looper.loop() 就这样一直在主线程守着,我们的 App 才有机会等待用户来操作,才不会执行完 main() 方法就结束了。
Looper.loop() 这个方法在主线程循环着,当有消息的时候就进行消息处理,可以调用我们在 Activity 里写的生命周期方法,也就说我们的代码其实就是在这个循环里面去执行的,当然不会阻塞了。
ActivityThread.java 的 main 函数是应用的入口,所以,直接看 ActiivtyThread.java 的 main 方法的最后。looper.loop() 是这样被调用的:
如题主所说 Looper.loop() 的内部有一个无限的循环,只要应用一直在跑,Looper.loop() 的循环就不会跳出来。由上面的代码可知,如果从 Looper.loop() 的循环跳出来,会抛出「
主线程循环异常退出」('
Main thread loop unexpectedly exited')的异常。
Android 的应用是由事件驱动的,looper.loop() 不断地接收事件、处理事件,每一个应用都是运行在 Looper.loop() 的控制之下,如果它停止了,应用也就停止了。只能是某一个应用阻塞 Looper.loop(),而不是 Looper.loop() 阻塞应用。
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
Android 的应用是由事件驱动的,looper.loop() 不断地接收事件、处理事件,每一个应用都是运行在 Looper.loop() 的控制之下,如果它停止了,应用也就停止了。只能是某一个应用阻塞 Looper.loop(),而不是 Looper.loop() 阻塞应用。
大神们写得都很好,但其实对我们小白来讲,还有很多点是需要打通的
APK程序的运行过程
首先,ActivityThread从static main()函数开始,调用prepareMainLooper()为UI线程创建一个消息对列(MessageQueue)
然后,创建一个ActivityThread对象,在其初始化代码中会创建一个H(Handler)对象和一个AppplicationThread(Binder)对象
Binder负责接远程AmS的IPC调用,收到消息后,通过Handler将消息发送到消息队列,UI主线程会异步的从消息队列中取出消息并执行操作
接着,UI主线程调用Looper.loop()进入消息循环体。
当ActivityThread接收到AmS发送start某个activit后,就会创建指定的Activity对象。Activity又会创建PhoneWindow类--->DecroView类--->相应的View创建完成后,Activity需要把创建好的界面显示到屏幕上,于是调用WindowManager,他会创建一个ViewRoot对象,创建完ViewRoot后,WM调用WmS提供的远程接口完成添加一个窗口并显示到屏幕上。
-------------------------以上摘自柯元旦<Android内核剖析>
我觉得比较重要的几个点:
1.基本上一个Binder会对应一个线程,因为他用于IPC通信,肯定是阻塞式的,他又不可能在UI线程中阻塞
2.理解AmS和和客户端Activity的交互过程,WmS拿到启动某个Activity的消息后肯定会把这条消息以IPC的方式发送给ActivityThread,负责这一过程的Binder就是ApplicationThread
3.如果不理解Binder请忽略
APK程序的运行过程
首先,ActivityThread从static main()函数开始,调用prepareMainLooper()为UI线程创建一个消息对列(MessageQueue)
然后,创建一个ActivityThread对象,在其初始化代码中会创建一个H(Handler)对象和一个AppplicationThread(Binder)对象
Binder负责接远程AmS的IPC调用,收到消息后,通过Handler将消息发送到消息队列,UI主线程会异步的从消息队列中取出消息并执行操作
接着,UI主线程调用Looper.loop()进入消息循环体。
当ActivityThread接收到AmS发送start某个activit后,就会创建指定的Activity对象。Activity又会创建PhoneWindow类--->DecroView类--->相应的View创建完成后,Activity需要把创建好的界面显示到屏幕上,于是调用WindowManager,他会创建一个ViewRoot对象,创建完ViewRoot后,WM调用WmS提供的远程接口完成添加一个窗口并显示到屏幕上。
-------------------------以上摘自柯元旦<Android内核剖析>
我觉得比较重要的几个点:
1.基本上一个Binder会对应一个线程,因为他用于IPC通信,肯定是阻塞式的,他又不可能在UI线程中阻塞
2.理解AmS和和客户端Activity的交互过程,WmS拿到启动某个Activity的消息后肯定会把这条消息以IPC的方式发送给ActivityThread,负责这一过程的Binder就是ApplicationThread
3.如果不理解Binder请忽略
messagequene里面的next又是个for(;;)死循环,继续深入下去jni层还有个for(;;),三层死循环呢,哪只楼主的一层啊,按楼主的意思得卡成翔。其实答案楼主的第二张图已经有提示了,next()后面写了两个很大的英文提示 ---- might block。。。。。。block不就意味着不占有cpu了么,为啥会卡死。。。。。最后一句,看看epoll就明白looper的原理了。再补充一下,实在不明白的话可以用libevent写个简单的10行例子代码跑跑。
就像一辆车在圆形赛车道上跑,一边跑一边执行任务,比如开到市中心买瓶水,任务完成又回到刚刚离开的地方,继续各种执行买水的任务,直到没有任务了,行了,跑一圈回到起点。睡觉,有任务叫醒你,你又开始跑一圈。边跑边接单。
大概的意思是,OnCreate等activity的生命周期函数都是由AMS(ActivityManagerService)通过往ApplicationThread这个binder上发消息,然后往MainLooper的messagequeue上发消息,主线程取出消息来执行这些生命周期的。
当MainLooper的messagequeue为空时,也可以注册idlehandler去执行空转,也就是没有消息是执行一种空闲时的动作。
如果涉及到UI的更新,会使用异步消息,平时我们handler发送得消息都是同步消息。异步消息的大概意思就是messagequeue查找的时候会跳过前面所有的同步消息,直接去取异步的来执行,这样就保证了UI更新的即时性,优先被主线程处理,
当MainLooper的messagequeue为空时,也可以注册idlehandler去执行空转,也就是没有消息是执行一种空闲时的动作。
如果涉及到UI的更新,会使用异步消息,平时我们handler发送得消息都是同步消息。异步消息的大概意思就是messagequeue查找的时候会跳过前面所有的同步消息,直接去取异步的来执行,这样就保证了UI更新的即时性,优先被主线程处理,