文章出處:鏈接: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運行過程的理解:**

* **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屏幕刷新機制