Handler到底是如何完成线程切换的?

本文深入剖析Handler机制,详述其如何通过Looper和MessageQueue实现跨线程消息传递,完成从子线程到主线程的消息处理过程。

Handler到底是如何完成线程切换的?这个问题要从Handler最初的用法和原理讲起。

首先我们列出正常情况下一个Handler使用的步骤然后讲解分析如何实现,这里不对一些基础的概念做解释,具体的请查阅源码。

Handler的使用步骤

1.调用Looper.prepare();

2.创建Handler对象;

3.调用Looper.Loop()方法。

4.线程中发送消息。

这样我们就创建好了一个Handler可以采用sendMessage等方法发送消息。

那么这几步都干了什么?

第一步:调用prepare方法

private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
}

创建了一个Looper对象保存在了一个ThreadLocal对象中,至于ThreadLocal是干什么的,查询有关ThreadLocal如何存储线程中数据的资料

上面代码中创建了一个Loper对象,new Looper的时候我们可以看到:

private Looper(boolean quitAllowed) {
      mQueue = new MessageQueue(quitAllowed);
      mThread = Thread.currentThread();
 }
// 创建了一个MessageQueue对象,这个就是一个消息队列用来保存我们发送的Message,其实这个MessageQUueue是一个单链表。

后边我们会知道这里创建了一个MessageQueue对象,从字面上来看就是一个消息队列,其实他是一个链表,链表的数据结构就是便于插入和删除。

第二步创建Handler

public Handler(Callback callback, boolean async) {
        ......
        mLooper = Looper.myLooper();
        ......
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

从构造方法中我们知道了Hanlder中获取了一个Looper对象,这个Looper中保存了一个mQueue.这个Lopper对象就是我们前面保存在ThreadLocal中的那个。

第三步调用loop()

public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();
        for (;;) {// 无限循环
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            ......
            final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            final long end;
            try {// 处理消息
                msg.target.dispatchMessage(msg);
            ......
    }

我们清楚的看到:loop()方法中获取了Looper对象,取出了消息队列mQueue,然后开始循环。同时调用了MessageQueue的next()方法取出一个消息最后调用了这个消息的dispathchMessage();看完源码我们知道这个msg.tagget.dispatchMessage()就是Handler中的消息处理的方法。

 public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

这个方法的代码中我们看到,首先判断了callback是不是null.这是因为Handler使用有两种方法,一种可以通过创建CallBack的方式。最后调用了Hanlder的handleMessage(msg);这个方法就是我们经常自己实现的消息处理方法。所以我们就到达了目标。

第四步:发送消息

调用sendMessage()等方法发送消息,追踪发送消息的方法,最后调用了以下的方法:

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }

这里拿到了我们前面Handler构造方法保存的mQueue,然后调用了enqueueMessage()方法。

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

最后看queue.enqueueMessage(msg, uptimeMillis);在queue中处理了消息。

boolean enqueueMessage(Message msg, long when) {
       synchronized (this) {// 同步的操作,同一时间只能一个线程操作
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }
            ......
            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {// 判断消息要执行的时间顺序
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;// 把消息插队链表
                needWake = mBlocked;
            } else {
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }
        }
        return true;
    }

也就是通过消息要执行的时间,最后把消息插入链表中。

这样我们就完成了消息的发送!

此刻:主线程的loop()方法一直在循环处理着消息,我们发送的消息就会到loop()方法中去,最后交给Handler处理。

那么到底是怎么样就完成了线程的切换呢?

其实就是这个消息发送的过程,我们在不同的线程发送消息,线程之间的资源是共享的。也就是任何变量在任何线程都可以修改,只要做并发操作就好了。上述代码中插入队列就是加锁的synchronized,Handler中我们使用的是同一个MessageQueue对象,同一时间只能一个线程对消息进行入队操作。消息存储到队列中后,主线程的Looper还在一直循环loop()处理。这样主线程就能拿到子线程存储的Message对象,在我们没有看见的时候完成了线程的切换。

所以总结来讲就是:

1.创建了一个Looper对象保存在ThreadLocal中。这个Looper同时持有一个MessageQueue对象。

2.创建Handler获取到Looper对象和MessageQueue对象。在调用sendMessage方法的时候在不同的线程(子线程)中把消息插入MessageQueue队列。

3.在主线程中(UI线程),调用Looper的loop()方法无限循环查询MessageQueue队列是否有消息保存了。有消息就取出来调用dispatchMessage()方法处理。这个方法最终调用了我们自己重写了消息处理方法handleMessage(msg);这样就完成消息从子线程到主线程的无声切换。

最后补充:我们经常使用Handler没有创建Looper调用Looper.pepare()和Looper.loop()是因为在ActivityThread中已经创建了主线程的Looper对象,保存在了ThreadLocal中,我们在创建Hander的时候就会从ThreadLocal中取出来这个Looper。

从主线程类ActivityThread的入口方法main()方法中我们可以清楚的看到系统帮我们如何实现的:

  public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        SamplingProfilerIntegration.start();

        CloseGuard.setEnabled(false);

        Environment.initForCurrentUser();

        // Set the reporter for event logging in libcore
        EventLogger.setReporter(new EventLoggingReporter());

        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);

        Process.setArgV0("<pre-initialized>");

        Looper.prepareMainLooper();// 调用了prepareMainLooper(),创建Looper和MessageQueue

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();// 获取Handler
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();// 开始调用loop()方法

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

 

<think>好的,我现在需要解决用户的问题:如何在Android子线程中创建多个Handler并管理它们的消息。用户还提到了多线程Handler的使用方法及注意事项。根据用户提供的引用内容和我之前的回答,我需要综合这些信息来给出详细的步骤和注意事项。 首先,我需要回顾用户提供的引用内容。引用[1]提到了两种创建Handler的方式:自定义Handler子类和匿名内部类。引用[4]和[5]提到了在子线程中创建Handler需要初始化Looper,例如使用Looper.prepare()和Looper.loop()。另外,引用[3]提到了HandlerThread的使用,这可能是一个简化子线程Handler创建的方法。 接下来,我需要考虑在子线程中创建多个Handler的情况。每个Handler需要关联到同一个Looper,因为每个线程只能有一个Looper。所以,如果用户想在子线程中创建多个Handler,这些Handler必须共享同一个Looper实例。这意味着子线程需要先调用Looper.prepare()初始化Looper,然后创建多个Handler实例,每个实例都使用该Looper。最后,调用Looper.loop()来启动消息循环。 然后,管理消息的问题。多个Handler处理不同的消息类型,可以通过Message的what字段来区分,或者为每个Handler分配不同的职责。需要确保消息发送到正确的Handler,避免消息混乱。此外,需要注意线程安全,因为多个Handler可能同时处理消息,特别是如果它们操作共享数据时,需要使用同步机制。 还要考虑内存泄漏的问题,尤其是在子线程中创建的Handler如果持有Activity的引用,可能导致无法回收。需要使用弱引用或者静态内部类来避免这种情况,如引用[1]和之前的回答中提到的SafeHandler示例。 另外,用户可能想知道如何正确停止子线程的消息循环。当不再需要Handler处理消息时,应该调用Looper.quit()或Looper.quitSafely()来终止消息循环,释放资源,避免内存泄漏。这在引用[4]的示例中有所涉及,但需要明确说明。 最后,是否需要使用HandlerThread来简化流程?HandlerThread是Android提供的一个已经封装了Looper的线程类,可以更方便地创建带有Looper的子线程,如引用[5]提到的HandlerThread.java。使用HandlerThread的话,创建多个Handler会更简单,因为它们可以共享同一个Looper而无需手动管理Looper的生命周期。 现在需要将这些思路整理成结构化的回答,分步骤说明创建方法、消息管理机制、注意事项,并给出代码示例。同时,要引用用户提供的引用内容中的相关信息,如引用[4]中的LooperThread示例,引用[1]中的Handler子类创建方式,以及引用[5]的HandlerThread使用。 可能还需要比较直接使用Looper和HandlerThread两种方法的优缺点,帮助用户选择适合的方案。此外,提醒用户关于线程安全和资源释放的问题,确保他们了解潜在的风险和最佳实践。 总结下来,回答的结构应包括: 1. 子线程中创建多个Handler的前提条件(初始化Looper) 2. 具体创建步骤(两种方法:手动管理Looper和使用HandlerThread) 3. 消息管理机制(区分消息类型,职责划分) 4. 注意事项(内存泄漏、资源释放、线程安全) 5. 代码示例 6. 相关问题扩展 需要确保回答清晰、详细,并覆盖用户可能关心的所有方面,同时引用正确的来源,如引用[4]中的LooperThread示例,引用[1]中的Handler创建方式等。还要注意使用中文,符合用户的要求,避免技术术语使用不当,并保持自然流畅。</think>### 如何在子线程中创建多个Handler并管理消息 --- #### 一、子线程中创建多个Handler的前提条件 1. **必须初始化Looper** Android子线程默认没有绑定`Looper`,需手动调用以下方法初始化消息循环系统[^4][^5]: ```java Looper.prepare(); // 创建Looper和MessageQueue Looper.loop(); // 启动消息循环 ``` - 每个线程**只能有一个Looper**,多个Handler必须共享同一个`Looper`对象[^4]。 - 若未调用`Looper.prepare()`直接创建Handler,会抛出`RuntimeException`("Can't create handler inside thread that has not called Looper.prepare()")[^5]。 --- #### 二、创建多个Handler的具体方法 ##### 方法1:手动管理Looper(基础实现) ```java // 子线程中创建多个Handler Thread workerThread = new Thread(() -> { Looper.prepare(); // 初始化Looper // 创建第一个Handler Handler handler1 = new Handler(Looper.myLooper()) { @Override public void handleMessage(Message msg) { if (msg.what == 1) { // 处理类型1的消息 } } }; // 创建第二个Handler Handler handler2 = new Handler(Looper.myLooper()) { @Override public void handleMessage(Message msg) { if (msg.what == 2) { // 处理类型2的消息 } } }; Looper.loop(); // 启动消息循环(阻塞当前线程) }); workerThread.start(); ``` ##### 方法2:使用HandlerThread(推荐) `HandlerThread`是Android提供的已封装`Looper`的线程类,简化流程并降低资源泄漏风险[^5]: ```java // 创建HandlerThread并启动 HandlerThread handlerThread = new HandlerThread("WorkerThread"); handlerThread.start(); // 基于HandlerThread的Looper创建多个Handler Handler handler1 = new Handler(handlerThread.getLooper()) { @Override public void handleMessage(Message msg) { // 处理handler1的消息 } }; Handler handler2 = new Handler(handlerThread.getLooper()) { @Override public void handleMessage(Message msg) { // 处理handler2的消息 } }; ``` --- #### 三、消息管理机制 1. **消息区分** - 通过`Message.what`字段定义消息类型,每个Handler处理特定类型的消息[^1]: ```java Message msg1 = Message.obtain(); msg1.what = 1; // 标记为handler1处理 handler1.sendMessage(msg1); Message msg2 = Message.obtain(); msg2.what = 2; // 标记为handler2处理 handler2.sendMessage(msg2); ``` - 或通过`Handler`对象直接区分:将不同任务分配给不同Handler实例。 2. **职责划分** - **Handler1**:负责网络请求结果处理 - **Handler2**:负责本地数据库操作回调 - **Handler3**:负责定时任务调度 --- #### 四、注意事项 1. **内存泄漏** - 若Handler持有`Activity`或`Fragment`的强引用,可能导致无法回收。使用**弱引用**或静态内部类[^1]: ```java static class SafeHandler extends Handler { private WeakReference<Activity> activityRef; SafeHandler(Activity activity) { activityRef = new WeakReference<>(activity); } @Override public void handleMessage(Message msg) { Activity activity = activityRef.get(); if (activity != null) { /* 安全操作 */ } } } ``` 2. **资源释放** - 子线程任务完成后必须调用`Looper.quit()`,否则线程会因`Looper.loop()`阻塞而无法终止[^4][^5]: ```java handlerThread.quitSafely(); // 安全退出HandlerThread ``` 3. **线程安全** - 多个Handler操作共享数据时,需使用`synchronized`或`AtomicBoolean`(引用[4]中的`AtomicBoolean`)保证原子性: ```java private AtomicBoolean isTaskRunning = new AtomicBoolean(false); ``` --- ### 代码示例(完整流程) ```java // 使用HandlerThread创建多个Handler public class WorkerThread extends HandlerThread { private Handler handler1; private Handler handler2; public WorkerThread() { super("WorkerThread"); } @Override protected void onLooperPrepared() { super.onLooperPrepared(); // 初始化Handler handler1 = new Handler(getLooper()) { @Override public void handleMessage(Message msg) { // 处理handler1的任务 } }; handler2 = new Handler(getLooper()) { @Override public void handleMessage(Message msg) { // 处理handler2的任务 } }; } public void sendTaskToHandler1(Runnable task) { handler1.post(task); } public void sendTaskToHandler2(Runnable task) { handler2.post(task); } } ``` --- ### 相关问题扩展 1. **如何确保多个Handler的消息顺序?** - `MessageQueue`按时间优先级排序,可通过`sendMessageDelayed()`控制时序[^2]。 2. **HandlerThread与普通Thread+Looper有何性能差异?** - `HandlerThread`内部已优化Looper生命周期管理,更适合长期运行的场景[^5]。 3. **Kotlin协程如何替代多Handler模式?** - 使用`CoroutineDispatcher`(如`Dispatchers.IO`)切换线程,结合`LiveData`更新UI,减少回调嵌套[^2]。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值