Handler机制及原理探究

本文详细探讨了Android中的Handler机制,从宏观上介绍了Handler、Looper和MessageQueue的角色及其关系。Handler主要用于线程间消息传递,与特定Thread和MessageQueue关联。文章通过代码示例展示了如何在不同线程中使用Handler,以及如何实现跨进程消息传递。Handler的运作依赖于Looper的无限循环来获取和分发消息。MessageQueue在Native层也有实现,涉及Message的插入和分发流程,包括Message的重用、同步栏(SyncBarrier)和IdleHandler的机制。文章旨在揭开Handler背后的实现原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Handler使用简单功能强大,常被用作线程间传递消息的组件,而且还可以用于跨进程。

消息机制背后有包括Looper ,MessageQueue管理和分发消息的实现,同时在Native层也单独实现了一套类似的机制,接收和处理Native层的消息。Java层和Native层的消息循环是独立运行的,彼此的Message并不会互通,Native使用epoll机制来实现监听及触发,并向JAVA层提供了接口。

这里从Java层开始深入探究下Handler和消息机制背后实现的原理。


初探Handler

本章只作为入口,先从宏观上去了解其中的架构,之后再做深入分析。

代码架构

Handler本身只负责发送和处理接收到的消息,其背后有一个消息循环为它管理和提供消息。
MessageQueue是管理着Message链表;而Looper是消息循环的主体,负责循环从MessageQueue中获取需要处理的新消息并向Handler输送。
这里写图片描述

其中MessageQueue有部分核心实现在native层(后续会讲到)。

MessageQueue.java
   private native static long nativeInit();
   private native static void nativeDestroy(long ptr);
   private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/
   private native static void nativeWake(long ptr);
   private native static boolean nativeIsPolling(long ptr);
   private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);

native方法实现在JNI层的android_os_MessageQueue.cpp:

android_os_MessageQueue.cpp
static const JNINativeMethod gMessageQueueMethods[] = {
    /* name, signature, funcPtr */
    { "nativeInit", "()J", (void*)android_os_MessageQueue_nativeInit },
    { "nativeDestroy", "(J)V", (void*)android_os_MessageQueue_nativeDestroy },
    { "nativePollOnce", "(JI)V", (void*)android_os_MessageQueue_nativePollOnce },
    { "nativeWake", "(J)V", (void*)android_os_MessageQueue_nativeWake },
    { "nativeIsPolling", "(J)Z", (void*)android_os_MessageQueue_nativeIsPolling },
    { "nativeSetFileDescriptorEvents", "(JII)V",
            (void*)android_os_MessageQueue_nativeSetFileDescriptorEvents },
};

什么是Handler?

打开Handler.java,先看下googler留给开发者留的一段说明:

/**
 * A Handler allows you to send and process {@link Message} and Runnable
 * objects associated with a thread's {@link MessageQueue}.  Each Handler
 * instance is associated with a single thread and that thread's message
 * queue.  When you create a new Handler, it is bound to the thread /
 * message queue of the thread that is creating it -- from that point on,
 * it will deliver messages and runnables to that message queue and execute
 * them as they come out of the message queue.
 * 
 * <p>There are two main uses for a Handler: (1) to schedule messages and
 * runnables to be executed as some point in the future; and (2) to enqueue
 * an action to be performed on a different thread than your own.
 * 
 * <p>Scheduling messages is accomplished with the
 * {@link #post}, {@link #postAtTime(Runnable, long)},
 * {@link #postDelayed}, {@link #sendEmptyMessage},
 * {@link #sendMessage}, {@link #sendMessageAtTime}, and
 * {@link #sendMessageDelayed} methods.  The <em>post</em> versions allow
 * you to enqueue Runnable objects to be called by the message queue when
 * they are received; the <em>sendMessage</em> versions allow you to enqueue
 * a {@link Message} object containing a bundle of data that will be
 * processed by the Handler's {@link #handleMessage} method (requiring that
 * you implement a subclass of Handler).
 * 
 * <p>When posting or sending to a Handler, you can either
 * allow the item to be processed as soon as the message queue is ready
 * to do so, or specify a delay before it gets processed or absolute time for
 * it to be processed.  The latter two allow you to implement timeouts,
 * ticks, and other timing-based behavior.
 * 
 * <p>When a
 * process is created for your application, its main thread is dedicated to
 * running a message queue that takes care of managing the top-level
 * application objects (activities, broadcast receivers, etc) and any windows
 * they create.  You can create your own threads, and communicate back with
 * the main application thread through a Handler.  This is done by calling
 * the same <em>post</em> or <em>sendMessage</em> methods as before, but from
 * your new thread.  The given Runnable or Message will then be scheduled
 * in the Handler's message queue and processed when appropriate.
 */

既然决定来了就不能放过每个细节,记录下重点。
运作方式:
每一个Handler实例只与一个单独的Thread和这个Thread的MessageQueue关联;
当我们在Thread中创建一个新的Handler时,会绑定这个Thread和Thread的MessageQueue,之后Handler允许我们向MessageQueue发送Message和Runnable,并在消息出列时处理它们。

Handler的2个主要用途:
1. 让Message和Runnable可以延迟执行;
2. 在另外一个线程中执行处理。

用法:
通过Post开头和sendMessage开头的方法可以发送消息到MessageQueue。
1. post开头的方法可以向队列插入Runnable;
2. sendMessage开头的方法则用于来送Message,Message将在handleMessage方法中被处理。
3. post和send方法既可以让消息“实时”被处理(相对于延时),也可以设置特定的时延,延时去处理。

建议:
应用进程中的的主线程是专门用于管理顶层的数据的,例如activity/广播/窗口等,不宜处理其他我们定义的耗时操作,因此我们应该创建自己的工作线程,通过Handler来向线程的MessageQueue发送要执行的任务。

三个需要理解的问题

看完上面这段话,有3个疑问需要探究:
1. Handler如何与Thread关联?
2. Thread和MessageQueue的关系是?
3. MessageQueue如何运作?它如何管理Runnable和Message?

后面对这些问题一一破解。


从如何使用Handler开始

怎么样才能使Handler正常运作?

例子1——定义在子线程的Handler

public class MainActivity extends AppCompatActivity {
   

    Handler mHandler;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Log.i("handler.demo", "Main Thread:" + Thread.currentThread().toString());
        new MyThread().start();
        //确保Handler已经在子线程中实例化
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                Log.i("handler.demo", "Running in Thread:" + Thread.currentThread().toString());
            }
        });
    }

    class MyThrea
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值