Handler机制浅析

本文深入浅析Android中的Handler机制,涉及Looper、Message及其相互关系。文章详细解释了Looper的线程绑定、Message作为消息载体的角色以及Handler在消息调度与处理中的作用。通过对源码的解读,阐述了三者如何协同工作,确保Android应用主线程的连续运行。

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

前言

Android应用程序是基础Java语言,内部封装了消息队列,然后在主线程开启了死循环不断获取消息并处理来实现不间断的界面变化与各种操作的执行。这个过程主要由:Looper、Handler、Message三者来共同完成,Message作为消息载体,Looper用于封装消息队列,Handler用于插入与处理消息。

Looper简析

首先,看看Looper类的几个成员变量

// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper;  // guarded by Looper.class
final MessageQueue mQueue;
final Thread mThread;

sThreadLoca并非是一个线程的实现版本,它并不是一个Thread,而是线程局部变量。简单地说,它是一种较为特殊的线程绑定机制。很显然,这里已经暴露了Looper与线程之间肯定有一些“特殊的联系”。

sMainLooper是个Looper对象,很显然Looper不可能是单例实现,并且从命名上我们也可以很容易看出来该对象,它可能是主线程/UI线程的Looper对象,咱们看到prepareMainLooper方法中初始化了这个对象

/**
 * Initialize the current thread as a looper, marking it as an
 * application's main looper. The main looper for your application
 * is created by the Android environment, so you should never need
 * to call this function yourself.  See also: {@link #prepare()}
 */
public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

从方法注释可以得知,该方法是实例化一个属于该应用主Looper(暂且这么理解),它是由系统自动调用,不需要你手动调用该方法

/**
 * Returns the application's main looper, which lives in the main thread of the application.
 */
public static Looper getMainLooper() {
    synchronized (Looper.class) {
        return sMainLooper;
    }
}

getMainLooper方法是提供获取主线程Looper对象,也就是说sMainLooper确实是属于主线程的Looper对象,并且它在应用开始时就被初始化了
具体的初始化位置于ActivityThread#main()方法中,有兴趣可自行了解

mQueue 是一个MessageQueue对象,咱们来看看官方对它的解释

/**
 * Low-level class holding the list of messages to be dispatched by a
 * {@link Looper}.  Messages are not added directly to a MessageQueue,
 * but rather through {@link Handler} objects associated with the Looper.
 * 
 * <p>You can retrieve the MessageQueue for the current thread with
 * {@link Looper#myQueue() Looper.myQueue()}.
 */
public final class MessageQueue {...}

我粗俗的翻译下,MessageQueue是一个低级类,通过Looper提供消息集合的派发,消息不会直接被添加到MessageQueue,而是通过Handler对象与其关联Looper对象。总结下,就是MessageQueue的作用只是简单是存储消息,具体的消息存取是由Handler与Looper来触发与管理。

之前咱们提到,Looper很可能与线程间存在密切联系。mThread成员变量再次证实了咱们的想法,怎么说?不急,咱们现在先分析下Looper中的核心方法

首先,prepare方法

 /** Initialize the current thread as a looper.
  * This gives you a chance to create handlers that then reference
  * this looper, before actually starting the loop. Be sure to call
  * {@link #loop()} after calling this method, and end it by calling
  * {@link #quit()}.
  */
public static void prepare() {
    prepare(true);
}
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对象,只要当prepare方法执行了之后对应线程中才可以创建Handler对象,并且必须确保在loop执行前调用。从代码中可以看出来,此时sThreadLocal对象被实例化并与当前线程绑定,What?你是想说sThreadLocal.set(new Looper(quitAllowed));就完成了绑定?从哪得出线程信息了?好,我们再来看看构造器。

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

OK,mThread = Thread.currentThread(),mThread现身了,引用了当前线程

loop方法

/**
 * Run the message queue in this thread. Be sure to call
 * {@link #quit()} to end the 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;

    // Make sure the identity of this thread is that of the local process,
    // and keep track of what that identity token actually is.
    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;
        }

        // This must be in a local variable, in case a UI event sets the logger
        Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }

        msg.target.dispatchMessage(msg);

        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }

        // Make sure that during the course of dispatching the
        // identity of the thread wasn't corrupted.
        final long newIdent = Binder.clearCallingIdentity();
        if (ident != newIdent) {
            Log.wtf(TAG, "Thread identity changed from 0x"
                    + Long.toHexString(ident) + " to 0x"
                    + Long.toHexString(newIdent) + " while dispatching to "
                    + msg.target.getClass().getName() + " "
                    + msg.callback + " what=" + msg.what);
        }

        msg.recycleUnchecked();
    }
}

注释已经给了明确的意思,它是开始循环派发当前线程的消息队列,可以调用quit停止。咱们再看看方法内部细节,在开始真正之前调用myLooper方法获取了sThreadLocal.get()判断若为空,表示当前线程并没有实例化过Looper,即并没有调用prepare方法,于是抛出异常。

Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
...
for (;;) {
...
final long newIdent = Binder.clearCallingIdentity();
}

这里涉及到IPC通信中的UID与PIC识别,存储于IPCThreadState中的mCallingPid与mCallingUid,简单的解释下。假设processA对processB进行了远程调用,processB必须携带processA的pid与uid,用于A端的权限校验。因此上述代码块的意思很明了:在每次派发消息前清除IPC身份标识,并判断身份是否变化

Message msg = queue.next(); // might block
if (msg == null) {
    // No message indicates that the message queue is quitting.
    return;
}

// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
    logging.println(">>>>> Dispatching to " + msg.target + " " +
            msg.callback + ": " + msg.what);
}

msg.target.dispatchMessage(msg);

if (logging != null) {
    logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}

获取队列中的下一个消息,获取Looper的日志输出对象。OK,开始派发消息,在处理消息的前后输出日志

需要注意的,loop方法中若成功执行,那里面执行for死循环,将会阻塞后续代码。即在loop调用后,后续的代码将永远执行不到,不信你看~

ActivityThread#main(String[] args)

public static void main(String[] args) {
...
    Looper.prepareMainLooper();
...
    Looper.loop();

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

Message浅析

Message,消息载体。包括:身份标识what、数值变量arg1与arg2(用于额外数据较简单的情况下使用)、数据obj(Object类型)、replyTo(用于IPC,与本篇内容)、数据data(Bunlde类型)、target对象(存储目标Handler对象)

/**
 * 
 * Defines a message containing a description and arbitrary data object that can be
 * sent to a {@link Handler}.  This object contains two extra int fields and an
 * extra object field that allow you to not do allocations in many cases.  
 *
 * <p class="note">While the constructor of Message is public, the best way to get
 * one of these is to call {@link #obtain Message.obtain()} or one of the
 * {@link Handler#obtainMessage Handler.obtainMessage()} methods, which will pull
 * them from a pool of recycled objects.</p>
 */
 public final class Message implements Parcelable {...}

obtain方法

/**
 * Return a new Message instance from the global pool. Allows us to
 * avoid allocating new objects in many cases.
 */
public static Message obtain() {
    ...
}
/**
 * Same as {@link #obtain()}, but copies the values of an existing
 * message (including its target) into the new one.
 * @param orig Original message to copy.
 * @return A Message object from the global pool.
 */
public static Message obtain(Message orig) {
    ...
}
/**
 * Same as {@link #obtain()}, but sets the value for the <em>target</em> member on the Message returned.
 * @param h  Handler to assign to the returned Message object's <em>target</em> member.
 * @return A Message object from the global pool.
 */
public static Message obtain(Handler h) {
    Message m = obtain();
    m.target = h;

    return m;
}

实现Message的复用,内部缓存是用一个链表形式的全局缓存池实现,一共有八个重载方法。各个方法的差异只是对于获取到的Message对象各信息赋值。

既然使用了缓存,那么肯定有回收消息的方法。对于被回收了消息,清除了对象信息后,若链表缓存池未满,会把该消息放到链表的首部。结合recycleUnchecked与obtain方法可以看出,这是个后进先出的链表。

public void recycle() {
    // check ...
    recycleUnchecked();
}
/**
 * Recycles a Message that may be in-use.
 * Used internally by the MessageQueue and Looper when disposing of queued Messages.
 */
void recycleUnchecked() {
    // Mark the message as in use while it remains in the recycled object pool.
    // Clear out all other details.
    flags = FLAG_IN_USE;
    what = 0;
    arg1 = 0;
    arg2 = 0;
    obj = null;
    replyTo = null;
    sendingUid = -1;
    when = 0;
    target = null;
    callback = null;
    data = null;

    synchronized (sPoolSync) {
        if (sPoolSize < MAX_POOL_SIZE) {
            next = sPool;
            sPool = this;
            sPoolSize++;
        }
    }
}

总结,Message对象作为消息内容的载体,它的主要核心就是实现了链表缓存与对于消息内容的存取与维护

Handler浅析

Handler,简单总结下源码注释
1.Handler用于处理与其自身关联的线程中的消息队列
2.一个Handler只对应一个Looper与一个线程
3.Handler的主要两个作用:消息的调度与处理;在不同线程间执行操作

/**
 * 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.
 */
public class Handler {...}

咱们主要看两方面内容
1.Handler与Looper之间的关联
2.Handler与Message之间的关联

/**
 * Use the {@link Looper} for the current thread with the specified callback interface
 * and set whether the handler should be asynchronous.
 *
 * Handlers are synchronous by default unless this constructor is used to make
 * one that is strictly asynchronous.
 *
 * Asynchronous messages represent interrupts or events that do not require global ordering
 * with represent to synchronous messages.  Asynchronous messages are not subject to
 * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
 *
 * @param callback The callback interface in which to handle messages, or null.
 * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
 * each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
 *
 * @hide
 */
public Handler(Callback callback, boolean async) {
    ...
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

在Handler实例化的时候,判断了当前线程对应的Looper对象是否已创建。这也证明了在分析Looper时对Looper与线程之间存在联系的判断,以及Looper与线程是一一对应的。

public final Message obtainMessage(...)
{
    ...
}

obtainMessage的四个重载方法,分别调用了相关的Message.obtain(…)

发送消息的两种方式:Message与Runnbale,这里我就拿delayed举例

public final boolean postDelayed(Runnable r, long delayMillis)
{
    return sendMessageDelayed(getPostMessage(r), delayMillis);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

从上面的代码可以看出,发送Runnable实质上也是Message对象,只不过Runnable方式会为Message对象的callback对象赋值

消息处理

/**
 * Handle system messages here.
 */
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}
/**
 * Subclasses must implement this to receive messages.
 */
public void handleMessage(Message msg) {
}

消息处理时首先会判断callback对象,若不为空则执行对应的回调代码。否则将空方法handleMessage进行处理,如果想要接收到消息后进行对应的事件操作,那么handleMessage是子类必须重写的方法。

三者关系

用一张图来解释他们三者之间的联系

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值