从源码简要分析Handler的使用

Handler我们经常会用到,一般可以用来处理延时任务,或者进行异步耗时操作的同时更新UI等。

在使用过程中,不知道大家是否和我以前初学的时候一样有这样的疑惑:

一般我们都是在UI线程新建static hander对象,并且实现了它的HandleMessage()方法,然后在其他任何地方通过sendMessage()方法发送的消息,最后都会被handleMessage()所处理,包括子线程里调用sendMessage()也可以。那么从sendMessage()到handleMessage(),这其中是经历了怎样的过程呢?

带着这个问题,我们来找源码进行分析。(看这篇文章之前,最好已经听过,或者了解过Handler,Looper, MessageQueue, Message这几个东西,如果没有,那就得多看看其他资料了。)

一、Handler的构造方法做了什么

首先,我们日常使用时,必须得先创建一个handler对象,那么我们看下它的默认构造方法,这是一定会被调用的:

    public Handler() {
        this(null, false);
    }

它直接调用了另外一个构造方法,看看这个构造方法:

public Handler(Callback callback, boolean async) {
    //对可能出现的内存泄漏进行警告
    if (FIND_POTENTIAL_LEAKS) {
        final Class<? extends Handler> klass = getClass();
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                (klass.getModifiers() & Modifier.STATIC) == 0) {
            Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                klass.getCanonicalName());
        }
    }

    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类是匿名类,内部类,或者本地类,则必须是static类型的,否则就会有警告。

(注:为什么必须是static类型呢?因为在Java里,匿名类,内部类,或者本地类如果不是静态的,则会隐式地持有一份对外部类的引用。比如我们在一个activity里新建了handler的匿名内部类,则它会隐式地持有此activity的引用。那么存在这样的隐患:有可能handler发送的message还没来得及处理,activity就被finish掉了。本来activity被finish之后是要回收的,但是因为handler还持有对它的引用,activity对象就无法被回收了,造成内存泄漏。)

好了继续看上面的构造方法,警告完内存泄漏之后,接下来是对Handler的几个成员变量做了赋值。其中mCallback和mAsynchronous都是传入的值,一个为null,一个是flase。

重点是mLooper和mQueue。
mLooper是个Looper类型的成员变量,它通过Looper.myLooper()得到。这个Looper.myLooper()是怎样的实现先不管,这里只要先了解Handler类里包含了一个Looper类型的成员变量和一个MessageQueue类型的成员变量mQueue,并且在构造方法中作了初始化就好。

二、Handler如何发送Message

好了,Handler对象创建完毕,接下来就可以使用了。一般我们都是调用它的sendMessage方法。Handler有多个同类型的方法,但是最终调用都是一个,这里就不细说了。下图表示了它们的调用关系:

最终调用的都是Handler的enqueueMessage方法(其中传入的queue就是handler的mQueue成员变量):

Handler还有个post()方法,传入的是个Runnable对象,其最终也是同样的嵌套调用。
Handler的post()方法的解释点这里

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this; //给要处理的message绑定了目标
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

上面做了个msg.target = this的操作,把发送的message的target赋值为当前的handler对象,这里后面会用到。

三、MessageQueue是如何处理Message的

继续追MessageQueue的enqueueMessage方法,看它做了什么:

boolean enqueueMessage(Message msg, long when) {
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }
    if (msg.isInUse()) {
        throw new IllegalStateException(msg + " This message is already in use.");
    }

    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 {
            //遍历消息队列里所有的消息,逐个比较它们的时间,把新消息插入合适的位置
            // Inserted within the middle of the queue.  Usually we don't have to wake
            // up the event queue unless there is a barrier at the head of the queue
            // and the message is the earliest asynchronous message in the queue.
            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;
        }

        // We can assume mPtr != 0 because mQuitting is false.
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

“enqueue message”这个词,从意思上看,就是把消息放入队列。实际上上面这个方法做的也就是这件事,把handler发送的message按时间顺序插入消息队列,也就是MessageQueue中。

具体是怎么做的呢?MessageQueue类里有个Message类型的成员变量mMessages,表示当前需处理的消息,而每一个Message对象都有一个Message类型的next成员变量,表示它的“下一个消息”,而这些消息的插入又是按时间排序的,因此所有这些消息就构成了一个按时间排序的队列。
经过上面的处理,新消息就插入了消息队列合适的位置。

四、Looper对MessageQueue做了什么处理

然后消息一个个排好顺序了,那怎么处理它们呢?我们知道平时都是Handler的handleMessage()进行处理的,所以搜一下,搜到handler的这个dispatchMessage()方法;

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

从上面还暂时看不出来之前我们分析的消息队列和Handler是怎么联系的,不过可以看到的是消息处理之前是怎么分发的:

1. 如果message对象定义了callback的话,则由callback进行处理。

2. 否则,如果定义handler的时候,传入了callback(这个callback类和message的callback类不是同一个类),则由这个callback调用其handleMessage()方法进行处理。

3. 仅当callback的handleMessage()返回false时,handler的handleMessage()才会得到执行。

好了言归正传,继续追查dispatchMessage()是在哪里被调用的,找到不止一处,但是实际我们想要的是这个:Looper类的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();
    }
}

五、ThreadLocal保证了Handler的MessageQueue和Looper的MessageQueue,是同一个对象

在上面方法里,最前面几行,得到了一个MessageQueue 对象:

    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;

但是这个MessageQueue对象,和之前hander发送的消息最后插入的那个MessageQueue,是不是同一个呢?这就要看final Looper me = myLooper()这句,拿到的Looper对象是哪来的了,看Looper类的myLooper()源码:

    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

sThreadLocal是Looper类的一个ThreadLocal类型的成员变量:

// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

ThreadLocal在这里的意义是,不同的线程,通过get()得到的ThreadLocal对象是不同的,而同一个线程里拿到的是同一个ThreadLocal对象。
既然有get(),那就应该有set(),继续看:

    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的构造方法:

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

ThreadLocal的set()是在Looper的prepare()方法里被调用的。
到了这里,再回头梳理下,Handler的构造方法里,就确定了它的mQueue,怎么确定的呢?回头再看下handler的构造方法里这么几行:

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;

这里得到mLooper和mQueue的方法,和Looper的loop()方法得到Looper对象和MessageQueue对象的方法,是一模一样的,那么很确定了:

Handler的构造方法里,确定了它的mQueue,并且最后传入了sendMessage()方法,然后由这个mQueue调用其enqueueMessage(),把消息插入队列。

然后Looper类的loop()方法里,再次获取到这个mQueue对象,然后逐个取出其中的消息进行分发,交由消息的callback,handler的callback或者handleMessage()方法进行处理。

六、新线程里如何使用Handler

还有个问题:Looper类的prepare()和loop()是在什么时候被调用的呢?这个就不分析源码了。答案是:
1. 如果我们需要在新建的线程里创建handler处理任务,则必须手动调用Looper.prepare()和Looper.loop()方法,而且必须像下面这样,:

class LooperThread extends Thread {
    public Handler mHandler;

    public void run() {
        Looper.prepare();// 步骤1

        mHandler = new Handler() {// 步骤2
            public void handleMessage(Message msg) {
                //消息处理逻辑
            }
        };

        Looper.loop(); // 步骤3
    }
}

步骤1,2,3的顺序是不能乱的。为什么呢?上面我们贴的源码已经说明了,Looper对象和MessageQueue对象被创建是在Looper.prepare()里,因此这个必须放前面。而Looper.loop()是个循环执行的过程,如果一直有消息的话就会一直运行,所以要放在后面。
2. 那为什么平时在UI线程创建handler对象的时候,不需要调用Looper.prepare()和Looper.loop()方法呢?
实际上,这两个方法,源码里已经调用了,在ActivityThread的main()方法里,可自行查看。

七、Hander的post()方法的分析:

使用Handler进行任务的处理时,可以用post()方法传入一个Runnable对象进行处理。post()方法源码:

    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

可见,传入的Runnable对象,被包装成了一个Message对象,作为这个Message对象的callback。然后还是通过sendMessage的方法发送消息。

所以要注意,如果handler对象是在UI线程创建的,则这个Runnable对象的run()方法里不能做耗时操作,因为run()也会运行在UI线程。

总结:

1. Handler用来发送message和处理message.
2. MessageQueue把handler发送的message按时间顺序排序。
3. Looper不断循环从MessageQueue中取出下一个message,然后交给对应的Handler进行分发和处理。
4. 同一个线程里,可以定义多个Handler,但是只会有一个Looper和一个MessageQueue。

另,创建新的Message对象时,用Message.obtain()方法,比new Message()要好,因为obtain()是从消息池取出被recycle()的消息,如果消息池为空再创建新消息。因此效率会高很多,也节省资源。

猜你喜欢

转载自blog.csdn.net/fenggering/article/details/81161129