文章出处:链接:https://www.zhihu.com/question/34652589/answer/90344494
要完全彻底理解这个问题,需要准备以下4方面的知识:
1. Process/Thread,
2. Android Binder IPC,
3. Handler/Looper/MessageQueue消息机制,
4. Linux pipe/epoll机制。
**总结一下楼主主要有3个疑惑:**
1. Android中为什么主线程不会因为Looper.loop()里的死循环卡死?
2. 没看见哪里有相关代码为这个死循环准备了一个新线程去运转?
3. Activity的生命周期这些方法这些都是在主线程里执行的吧,那这些生命周期方法是怎么实现在死循环体外能够执行起来的?
*****
针对这些疑惑,
[@hi大头鬼hi](https://www.zhihu.com/people/50b28faaec835947511e005cd6734484)、[@Rocko](https://www.zhihu.com/people/4897651cf72b010294463a1fd113af52)、[@陈昱全](https://www.zhihu.com/people/53013cf8f04bf168814156e563beb935)
大家回答都比较精炼,接下来我再更进一步详细地一一解答楼主的疑惑:
**(1) Android中为什么主线程不会因为Looper.loop()里的死循环卡死?**
这里涉及线程,先说说说进程/线程,
* **进程**:每个app运行时前首先创建一个进程,**该进程是由Zygote fork出来的,用于承载App上运行的各种Activity/Service等组件**。
进程对于上层应用来说是完全透明的,这也是google有意为之,让App程序都是运行在Android Runtime。
大多数情况一个App就运行在一个进程中,除非在AndroidManifest.xml中配置`Android:process`属性,或通过JNI在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()`中**:
~~~java
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");
}
~~~
**thread.attach(false);便会创建一个Binder线程(具体是指ApplicationThread,Binder的服务端,用于接收系统服务AMS发送来的事件),该Binder线程通过Handler将Message发送给主线程**,具体过程可查看 [startService流程分析](https://link.zhihu.com/?target=http%3A//gityuan.com/2016/03/06/start-service/),这里不展开说,简单说Binder用于进程间通信,采用C/S架构。关于binder感兴趣的朋友,可查看我回答的另一个知乎问题: [为什么Android要采用Binder作为IPC机制? - Gityuan的回答](https://www.zhihu.com/question/39440766/answer/89210950)
另外,**[ActivityThread](https://www.androidos.net.cn/android/7.1.1_r28/xref/frameworks/base/core/java/android/app/ActivityThread.java)实际上并非线程,只是运行在主线程的对象**,不像HandlerThread类,**ActivityThread并没有真正继承Thread类,只是往往运行在主线程,给人以线程的感觉,其实承载ActivityThread的主线程就是由Zygote fork而创建的进程**。
* **主线程的死循环一直运行是不是特别消耗CPU资源呢?**
其实不然,这里就涉及到**Linux pipe/epoll机制**,简单说就是在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,详情见[Android消息机制1-Handler(Java层)](https://link.zhihu.com/?target=http%3A//www.yuanhh.com/2015/12/26/handler-message-framework/%23next),此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。
这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。 **所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。**
**(3) Activity的生命周期是怎么实现在死循环体(Looper.loop()里的死循环)外能够执行起来的?**
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运行过程的理解:**
![](https://picb.zhimg.com/80/7fb8728164975ac86a2b0b886de2b872_720w.jpg?source=1940ef5c)
* **system\_server进程是系统进程**
Java FrameWork框架的核心载体,里面运行了大量的系统服务,比如这里提供ApplicationThreadProxy(简称ATP),ActivityManagerService(简称AMS),这个两个服务都运行在system\_server进程的不同线程中,由于ATP和AMS都是基于IBinder接口,都是binder线程,binder线程的创建与销毁都是由binder驱动来决定的。
* **App进程则是我们常说的应用程序**,主线程主要负责Activity/Service等组件的生命周期以及UI相关操作都运行在这个线程; 另外,每个App进程中至少会有两个binder线程 [ApplicationThread](https://www.androidos.net.cn/android/8.0.0_r4/xref/frameworks/base/core/java/android/app/ActivityThread.java)(简称AT,它是ActivityThread的内部类)和ActivityManagerProxy(简称AMP,它是[ActivityManagerNative](https://www.androidos.net.cn/android/7.1.1_r28/xref/frameworks/base/core/java/android/app/ActivityManagerNative.java)的内部类),除了图中画的线程,其中还有很多线程,比如signal catcher线程等,这里就不一一列举。
Binder用于不同进程之间通信,由一个进程的Binder客户端向另一个进程的服务端发送事务,比如图中线程2向线程4发送事务;而handler用于同一个进程中不同线程的通信,比如图中线程4向主线程发送消息。
**结合图说说Activity生命周期,比如暂停Activity,流程如下:**
1. 线程1的AMS中调用线程2的ATP;(由于同一个进程的线程间资源共享,可以相互直接调用,但需要注意多线程并发问题)
2. 线程2通过binder传输到App进程的线程4;
3. 线程4通过handler消息机制,将暂停Activity的消息发送给主线程;
4. 主线程在looper.loop()中循环遍历消息,当收到暂停Activity的消息时,便将消息分发给ActivityThread.H.handleMessage()方法,再经过方法的调用,最后便会调用到Activity.onPause(),当onPause()处理完后,继续循环loop下去。
- 前言
- Android系统的体系结构
- Dalvik VM 和 JVM 的比较
- Android 打包应用程序并安装的过程
- Android ADB工具
- Android应用开发
- Android UI相关知识总结
- Android 中window 、view、 Activity的关系
- Android应用界面
- Android中的drawable和bitmap
- AndroidUI组件adapterView及其子类和Adapter的关系
- Android四大组件
- Android 数据存储
- SharedPreference
- Android应用的资源
- 数组资源
- 使用Drawable资源
- Material Design
- Android 进程和线程
- 进程
- 线程
- Android Application类的介绍
- 意图(Intent)
- Intent 和 Intent 过滤器(Google官网介绍)
- Android中关于任务栈的总结
- 任务和返回栈(官网译文)
- 总结
- Android应用安全现状与解决方案
- Android 安全开发
- HTTPS
- 安卓 代码混淆与打包
- 动态注入技术(hook技术)
- 一、什么是hook技术
- 二、常用的Hook 工具
- Xposed源码剖析——概述
- Xposed源码剖析——app_process作用详解
- Xposed源码剖析——Xposed初始化
- Xposed源码剖析——hook具体实现
- 无需Root也能Hook?——Depoxsed框架演示
- 三、HookAndroid应用
- 四、Hook原生应用程序
- 五、Hook 检测/修复
- Android 应用的逆向与加固保护技术
- OpenCV在Android中的开发
- Android高级开发进阶
- 高级UI
- UI绘制流程及原理
- Android新布局ConstraintLayout约束布局
- 关键帧动画
- 帧动画共享元素变换
- Android异步消息处理机制完全解析,带你从源码的角度彻底理解
- Android中为什么主线程不会因为Looper.loop()里的死循环卡死?
- 为什么 Android 要采用 Binder 作为 IPC 机制?
- JVM 中一个线程的 Java 栈和寄存器中分别放的是什么?
- Android源码的Binder权限是如何控制?
- 如何详解 Activity 的生命周期?
- 为什么Android的Handler采用管道而不使用Binder?
- ThreadLocal,你真的懂了吗?
- Android屏幕刷新机制