Looper.loop死循环为什么不会卡死


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");
}
thread.attach(false);便会创建一个Binder线程(具体是指ApplicationThread,Binder的服务端,用于接收系统服务AMS发送来的事件),该Binder线程通过Handler将Message发送给主线程,具体过程可查看 startService流程分析,这里不展开说,简单说Binder用于进程间通信,采用C/S架构。关于binder感兴趣的朋友,可查看我回答的另一个知乎问题:
另外, 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. 线程1的AMS中调用线程2的ATP;(由于同一个进程的线程间资源共享,可以相互直接调用,但需要注意多线程并发问题)
  2. 线程2通过binder传输到App进程的线程4;
  3. 线程4通过handler消息机制,将暂停Activity的消息发送给主线程;
  4. 主线程在looper.loop()中循环遍历消息,当收到暂停Activity的消息时,便将消息分发给ActivityThread.H.handleMessage()方法,再经过方法的调用,最后便会调用到Activity.onPause(),当onPause()处理完后,继续循环loop下去。

<think>我们被要求详细解释Android中的Handler机制工作原理。根据用户提供的引用内容,我们可以整合出一个全面的解释。Handler机制是Android中用于线程间通信的重要机制,尤其常用于子线程执行任务后更新UI(因为UI操作必须在主线程进行)。核心组件:1.Handler:负责发送和处理消息。2.Message:消息的载体,包含需要传递的数据。3.MessageQueue:消息队列,用于存储Handler发送的消息(实际上是一个按时间排序的优先级队列)。4.Looper:循环取出MessageQueue中的消息,并分发给对应的Handler处理。5.ThreadLocal:用于每个线程存储自己的Looper,实现线程隔离。工作流程:1.首先,在需要创建Handler的线程中,必须先调用Looper.prepare()(主线程在启动时已经初始化了Looper,所以不需要手动调用)。2.Looper.prepare()会创建一个Looper对象,并将其存储到当前线程的ThreadLocal中。同时,Looper内部会创建一个MessageQueue。3.然后调用Looper.loop(),开始循环从MessageQueue中取出消息。4.创建Handler对象,Handler在创建时会绑定当前线程的Looper(通过ThreadLocal获取)以及Looper中的MessageQueue。5.当我们在其他线程中调用Handler的sendMessage()方法时,Handler会将Message放入其绑定的MessageQueue中。6.Looper在循环中不断从MessageQueue中取出消息(如果队列为空则阻塞),然后调用该消息的target(即发送该消息的Handler)的dispatchMessage()方法。7.Handler的dispatchMessage()方法会调用handleMessage()方法,在该方法中处理消息(通常在这里更新UI)。注意点:-一个线程只能有一个Looper,但可以有多个Handler,这些Handler都绑定同一个Looper(也就是同一个线程的Looper)。-主线程(UI线程)在启动时已经调用了Looper.prepareMainLooper()和Looper.loop(),所以我们可以在主线程直接创建Handler。-在子线程中创建Handler,必须先调用Looper.prepare(),然后调用Looper.loop(),否则会报错。源码层面关键点(参考引用[3]和[4]):-Handler发送消息最终会调用到MessageQueue的enqueueMessage方法,将消息插入队列。-Looper.loop()方法中有一个死循环,不断调用MessageQueue的next()方法获取消息,然后通过msg.target.dispatchMessage(msg)分发消息。-消息处理完毕后,系统会回收消息(Message有消息池机制,避免频繁创建对象)。数学描述:假设消息队列为$Q$,消息入队操作$enqueue(Q,m)$,出队操作$dequeue(Q)$。Looper循环可表示为:$$\text{while}(m=dequeue(Q))\neq\text{null}\text{do}\text{process}(m)$$其中$m$为消息,$process(m)$即处理消息。下面我们按步骤详细解释。</think>### Android Handler机制工作原理详解 Handler机制是Android线程间通信的核心框架,主要用于后台线程执行任务后更新UI。其工作原理可分为以下关键组件和流程: --- #### 核心组件与关系 ```mermaid graph TD A[Handler] -->|发送消息| B(MessageQueue) B -->|存储消息| C[Message] D[Looper] -->|循环取出| B D -->|分发消息| A E[ThreadLocal] -->|存储Looper| D ``` 1. **Handler** - 作用:发送消息(`sendMessage()`)和处理消息(`handleMessage()`) - 关键特性:绑定到创建它的线程的Looper - 发送消息流程: ```java handler.sendMessage(msg) → MessageQueue.enqueueMessage() → Looper.loop() → handler.dispatchMessage() ``` 2. **MessageQueue** - 数据结构:基于时间优先级的单链表(`Message`对象链表) - 核心方法: - `enqueueMessage()`:插入消息(按`when`时间排序) - `next()`:阻塞式获取下一条消息(当队列空时进入休眠) 3. **Looper** - 作用:消息循环泵,不断从MessageQueue取出消息分发 - 关键方法: - `prepare()`:初始化当前线程的Looper(存入ThreadLocal) - `loop()`:启动消息循环(死循环调用`queue.next()`) - 主线程Looper初始化: ```java // ActivityThread.main() Looper.prepareMainLooper(); Looper.loop(); ``` 4. **ThreadLocal** - 作用:线程私有数据存储,确保每个线程有独立的Looper - 实现原理:每个线程的`Thread.threadLocals`字段存储专属数据 --- #### 完整工作流程 1. **消息发送** - 子线程调用`handler.sendMessage(msg)` - Handler将消息插入关联的MessageQueue: ```java // Handler.java public boolean sendMessage(Message msg) { return enqueueMessage(queue, msg, uptimeMillis); } ``` 2. **消息存储** - MessageQueue按`msg.when`(触发时间)排序插入链表 - 若队列空或新消息最早,唤醒Looper线程 3. **消息轮询** - Looper.loop() 循环调用`Message msg = queue.next()` - `next()`方法逻辑: - 有消息 → 立即返回 - 无消息 → 调用`nativePollOnce()`进入休眠(Linux epoll机制) 4. **消息分发** - Looper调用`msg.target.dispatchMessage(msg)` - Handler处理消息的优先级: ```mermaid graph LR A[Message.callback] -->|Runnable对象| B B[Handler.mCallback] -->|Callback接口| C C[handleMessage] -->|默认处理| ``` 5. **资源回收** - 消息处理完成后调用`msg.recycleUnchecked()` - Message加入对象池复用(避免频繁GC) --- #### 数学建模 消息队列操作可表示为: $$ \text{dequeue}(Q) = \begin{cases} \text{msg}_i & \text{if } \exists \text{msg}_i \text{ where } t_{\text{msg}_i} \leq t_{\text{now}} \\ \text{wait}(\Delta t) & \text{otherwise} \end{cases} $$ 其中: - $Q$:消息队列 - $t_{\text{msg}_i}$:消息触发时间 - $\Delta t$ = $ \min(t_{\text{msg}_j}) - t_{\text{now}} $ --- #### 关键设计思想 1. **线程隔离** 通过ThreadLocal实现各线程独立的Looper,避免并发冲突[^2]。 2. **阻塞唤醒优化** 使用Linux epoll机制实现空闲时CPU休眠,节省资源[^4]。 3. **对象复用机制** Message池(sPool)减少对象创建开销(类似享元模式)。 4. **线程切换封装** 开发者无需直接操作线程,通过Handler自动切换执行线程。 > 示例:子线程更新UI > ```java > new Thread(() -> { > Message msg = handler.obtainMessage(); > msg.obj = "Data from background"; > handler.sendMessage(msg); // 自动切换到主线程处理 > }).start(); > ``` --- #### 常见问题解析 **为什么主线程不会Looper.loop()卡死?** - 消息处理是分段执行的,每条消息处理时间必须短(否则ANR) - 空闲时调用`nativePollOnce()`释放CPU资源 **内存泄漏场景**: 匿名Handler持有外部Activity引用 → 解决方案: ```java // 使用静态内部类 + WeakReference static class SafeHandler extends Handler { WeakReference<Activity> ref; SafeHandler(Activity activity) { ref = new WeakReference<>(activity); } @Override public void handleMessage(Message msg) { Activity activity = ref.get(); if (activity != null) { /* 更新UI */ } } } ``` Handler机制通过以上设计,实现了高效、安全的跨线程通信,是Android系统流畅运行的基础[^1][^3][^4]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值