首页 > 代码库 > 弄明白handler机制

弄明白handler机制

一、Looper

首先,你得知道一个类,Looper类,顾名思义,Looper就是循环者的意思,那么Looper类的存在就是为了让一个普普通通的线程变成一个会循环执行的线程,我们可以理解为长生不老药,吃了就能不老。

 

普通的Thread类只需要执行Looper.prepare()方法就可以循环执行了。

prepare()方法是Looper类的静态方法,如下:


public class Looper {
    private static final String TAG = "Looper";
 
    // sThreadLocal.get() will return null unless you've called prepare().
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
 
    final MessageQueue mQueue;
    final Thread mThread;
    volatile boolean mRun;
 
    private Printer mLogging = null;
    private static Looper mMainLooper = null;  // guarded by Looper.class
 
     /** 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() {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper());
    }

 

sThreadLocal 是线程的独立变量副本,这里是用来保存线程的Looper对象的。

这里,我们先知道一条规则:每个人只能吃一颗长生不老药,in other words ,每个线程只能拥有一个关联的Looper对象,可以没有,但是最多只能拥有一个。

通过get()方法来判断是否有,没有则创建新的,有则抛出异常。为什么抛出异常呢?因为prepare是一个线程第一次使用的,也只能用一次。因此在用它之前应该是没有Looper对象的。有证明重复了。

通过调用ThreadLocal的set方法把Looper对象与当前线程关联起来。(如果你不明白ThreadLocal是干嘛的,你这里可以理解为一个map, map.put(this.thread, new Looper()),key就是当前线程,value就是Looper,如此关联)。

 

注意到Looper类采用默认构造方法,在new一个 Looper对象的时候,会创建一个消息队列

MessageQueue 这里的名字叫mQueue。

 

 

2、调用 loop()方法开始消息循环:

Loop()方法的源码如下

public static void loop() {
        Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        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();
       
        while (true) {
            Message msg = queue.next(); // might block
            if (msg != null) {
                if (msg.target == null) {
                    // No target is a magic identifier for the quit message.
                    return;
                }
 
                long wallStart = 0;
                long threadStart = 0;
 
                // 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);
                    wallStart = SystemClock.currentTimeMicro();
                    threadStart = SystemClock.currentThreadTimeMicro();
                }
 
                msg.target.dispatchMessage(msg);
 
                if (logging != null) {
                    long wallTime = SystemClock.currentTimeMicro() - wallStart;
                    long threadTime = SystemClock.currentThreadTimeMicro() - threadStart;
 
                    logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
                    if (logging instanceof Profiler) {
                        ((Profiler) logging).profile(msg, wallStart, wallTime,
                                threadStart, threadTime);
                    }
                }
 
                // 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.recycle();
            }
        }
    }


  MessageQueue queue = me.mQueue;

这几行代码是先获取到当前线程的消息队列,然后有两行代码是确保当前线程是在当前进程里的。可以忽略不管。接着,while(true)进入死循环。

   Message msg =queue.next(); // might block

通过消息队列的next()方法获得需要处理的消息,注意到后面的注释,当前线程在没有需要处理的消息时,会在next()方法中进入睡眠等待状态。

 

至于next()方法是如何判断是否有消息还是睡眠的过程,这里略过。可以通过查看源码了解到的。

 

好的,现在消息队列有了,那么线程A如何才能够向别的线程发送一个消息到线程B的消息队列中去呢?线程B收到这个消息后怎么如何处理它?如果这个时候,android设计者提供了一个专门用来发送和处理消息的handler类。

 

二、Handler

 final MessageQueue mQueue;
    final Looper mLooper;
    final Callback mCallback;
    IMessenger mMessenger;

Handler类里面有上述四个成员变量。其中我们需要知道的是消息队列变量 mQueue和循环者对象 mLooper

handler类里面有很多个发送消息的方法。

   //第1个方法
    public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }
    //第2个方法
    public final boolean sendEmptyMessage(int what)
    {
        return sendEmptyMessageDelayed(what, 0);
    }
   //第3个方法
    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }


也有两个处理消息的方法

 /**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) {
    }
   
    /**
     * 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);
        }
    }
 


其中handleMessage是用来子类对象重写该方法来接收并处理消息。

dispatchMessage是被用来处理系统的消息。

 

弄明白handler机制