Looper.loop()引发的惨案

本文详细分析了一位开发者在Android应用中遇到的问题:账号被挤下线后重新登录,请求执行但无回调。经过排查,发现问题是由于Looper.loop()导致的线程池串行执行,使得新任务无法执行。文章介绍了Looper.loop()的工作原理,并提出了避免此类问题的解决方案和对RxJava线程池设计的思考。

1、案件描述

在一个安静的下午,一妹子在`技术交流群里反馈(群号:977438066),自己开发的app,账号被挤下线时,重新登录到首页后,发现有一个请求,代码执行了,却没有任何回调,看得出,妹子很着急。

what ??? 还有这种事?原本安静的群,一下活跃了起来,男同胞们一顿狂猜,我总结了下,如下:

  • 会不会请求代码没执行,妹子自己搞错了吧?

  • 发请求前,出现异常,代码被中断运行?

  • 请求过程伴随着页面跳转,导致页面销毁时,请求被自动关闭?

  • 请求过程出现异常,被RxJava全局异常捕获了,并吃掉了,所以收不到失败回调?

这里解释下,妹子采用RxHttp+RxJava结合的方式发请求

经过第一轮询问后,以上猜想轻而易举的被推翻了,我也大概知道了案件的细节,为此,我用代码来还原一下,为简化案件,还原时,我会适当的做出修改,但意思还是那个意思。

2、案件还原

妹子在首页MainActivityOnCreate方法,会并行3个请求,如下:

@Override                                                              
protected void onCreate(Bundle savedInstanceState) {                   
    super.onCreate(savedInstanceState);                                
    setContentView(R.layout.main_activity);                            
    request1();                                                        
    request2();                                                        
    request3();                                                        
}                                                                      

public void request1() {                                               
    RxHttp.get("/service/...")                                         
        .asString()                                                    
        .to(RxLife.toMain(this))  //页面销毁,自动关闭请求,并在UI线程回调                        
        .subscribe(s -> {                                              
            //成功回调                                                     
        }, throwable -> {                                              
            //异常回调                                                     
        });                                                            
}                                                                      

public void request2() {                                               
    //省略请求代码,请求代码类似request1()方法                                        
}                         
<think>我们正在讨论Looper.loop()方法的功能。根据引用内容,我们可以总结出以下几点: 1. Looper.loop()方法用于启动一个消息循环,它会不断从消息队列中取出消息并处理。 2. 该方法会阻塞当前线程(如果没有消息时),直到有消息到来或者循环被终止。 3.Android中,主线程(UI线程)在启动时就已经调用了Looper.loop(),所以主线程不会退出,而是持续处理消息(包括UI事件等)。 4.子线程中,如果需要处理消息(例如通过Handler),则需要先调用Looper.prepare()创建Looper,然后调用Looper.loop()启动循环。 5. 如果不调用Looper.loop(),即使创建了Looper,消息也不会被处理,而且子线程会执行完run方法后结束。 现在,根据用户的问题,他想了解Looper.loop()方法的具体功能。我们可以这样组织回答: 1. 首先,简要说明Looper.loop()的作用:启动消息循环。 2. 然后,详细解释其内部工作流程。 3. 接着,说明它在主线程和子线程中的不同应用。 4. 最后,提醒使用注意事项(如资源释放)。 同时,我们需要在回答中自然地引用提供的参考内容。 </think>### Looper.loop() 方法的功能解析 #### 一、核心作用:启动消息循环 `Looper.loop()` 是 Android 消息机制的核心方法,其本质是**启动一个无限循环的消息泵**,用于持续处理消息队列中的任务[^2][^3]。 ```java // 简化源码逻辑 public static void loop() { while (true) { // 阻塞式获取消息(无消息时线程休眠) Message msg = queue.next(); // 将消息分发给对应的Handler处理 msg.target.dispatchMessage(msg); } } ``` - **消息驱动**:通过 `while(true)` 循环监听消息队列,保证线程持续运行[^2]。 - **阻塞优化**:当消息队列为空时,通过 `epoll` 机制让线程休眠,避免 CPU 空转[^3]。 --- #### 二、工作流程详解 消息处理分为四个阶段: 1. **消息获取** 调用 `MessageQueue.next()` 从队列头部取出消息(线程级 FIFO 队列)[^3]。 2. **消息分发** 通过 `msg.target.dispatchMessage()` 将消息路由到发送该消息的 `Handler`[^2][^4]。 3. **消息处理** 在 Handler 的 `handleMessage()` 中执行具体逻辑(**执行位置由 Handler 绑定的 Looper 所在线程决定**)[^1][^4]。 4. **消息回收** 处理完成后调用 `msg.recycleUnchecked()` 回收消息对象,避免重复创建[^4]。 --- #### 三、不同线程中的行为差异 | 线程类型 | `loop()` 特性 | 典型场景 | |------------|----------------------------------------|------------------------------| | **主线程** | 默认启动(`ActivityThread.main()`) | 处理 UI 事件、生命周期回调等[^4] | | **子线程** | 需手动调用(需先执行 `Looper.prepare()`) | 后台任务调度、跨线程通信[^3] | > ✅ **关键特性**: > - 主线程因 `loop()` 持续运行而不会退出[^3]。 > - 子线程调用 `loop()` 后需显式退出(`Looper.quitSafely()`),否则线程永不终止[^3]。 --- #### 四、资源管理与注意事项 1. **避免内存泄漏** ```java // 子线程使用后必须退出循环 new Thread(() -> { Looper.prepare(); Handler handler = new Handler(); Looper.loop(); // 线程阻塞在此处 // 需调用 Looper.myLooper().quitSafely() 才能执行后续代码 }).start(); ``` 2. **阻塞位置** `loop()` 之后的代码在循环退出前永不执行(如线程内 `loop()` 后的日志不会打印)[^3]。 3. **异常处理** 消息处理中的异常会触发 `Looper` 的 `mLogging` 回调,但不会终止循环[^4]。 --- #### 五、与 `Looper.prepare()` 的协同关系 $$ \text{子线程消息循环流程} = \underbrace{\text{Looper.prepare()}}_{\text{创建消息队列}} \rightarrow \underbrace{\text{创建 Handler}}_{\text{绑定队列}} \rightarrow \underbrace{\text{Looper.loop()}}_{\text{启动循环}} $$ - `prepare()`:初始化线程专属的 `Looper` 和 `MessageQueue`(通过 `ThreadLocal` 存储)[^1][^3]。 - `loop()`:激活消息泵,使 `Handler` 的消息处理逻辑生效[^2][^3]。 > **总结**:`Looper.loop()` 是 Android 异步通信的枢纽,它通过「阻塞-唤醒」机制实现线程的高效消息调度,其执行位置(主线程/子线程)由 `Looper` 的初始化位置决定[^1][^3][^4]。 --- ### 相关问题
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值