您的位置:首页 > 移动开发 > Android开发

Android异步消息之Looper、Handler、Message、HandlerThread的关系

2015-08-04 10:22 686 查看
转载请注明 /article/1462868.html

刚开始接触Android的时候,就碰到Handler了,然后大概知道这个类是用来异步更新UI的。但是事实上,Handler的功能远远不止于此。为了能好好的使用它,我觉得还是要仔细研究下它和与它相关的几个类,并总结一下。

1.概念

为了给大家瞬间的带入感,它们相互之间的逻辑关系,我先直接在这里总结罗列下,权当给大家热热身。

1. Looper

Looper的作用是绑定当前所在的线程thread,并创建一个MessageQueue,不断循环从MessageQueue读取消息,然后消息交给target(Handler)处理。

2. Handler

Handler的作用是获取当前所在线程的looper和其MessageQueue,并可发送消息到queue,然后再因message target指向其本身,辗转反侧回到自己这里进行处理,处理过程在当前线程中执行。

3. Message

Message是个载体,用来存储数据和指定target Handler。然后可以发送给那个Handler。

介绍完这三个,我们再来点刺激的,加深下印象!~~

故事是这样起头的,有个Looper的男人,他苦逼的开通了一个银行卡充钱提醒业务,就是messagequeue,用来干嘛呢?用来接收他女人需要开销的通知,然后类,当然是要打钱进去,最后这笔钱给那个她花。

Handler不用说,你们也猜到了,就是那个女人了。她通过男人的联系方式,发送需要money的提醒,然后从银行卡提现消费,美其名曰交给她处置。

Message呢,就类似是一条短信,一个电话啥的。

这样比喻下,是不是更有印象了~~

咳咳,言归正传。上述三个其实需要一个关键先生,那就是thread。thread是它们赖以生存的环境。没有thread就无法创建looper,没有thread,就无法执行handler的消息处理。

接着说说HandlerThread

HandlerThread相当于是Thread和Looper的合体。HandlerThread本身继承自Thread,并自己内部创建Looper与其关联,创建了looper就创建了MessageQueue。然后我们可以创建Handler与HandlerThread创建的Looper进行关联。

好了,热身完毕~~准备贴码分析。

2.部分源码分析

我们先根据Looper类的注释里的例子的流程进行讲解,因为这例子很普遍,很经典。


*  class LooperThread extends Thread { 

  *      public Handler mHandler; 

  * 

  *      public void run() { 

  *          Looper.prepare(); 

  * 

  *          mHandler = new Handler() { 

  *              public void handleMessage(Message msg) { 

  *                  // process incoming messages here 

  *              } 

  *          }; 

  * 

  *          Looper.loop(); 

  *      } 

  *  }


从这部分代码可以看到,先是创建一个线程,这样就有了运行的环境。

然后开始先是
Looper.prepare();
,我们代码跟进去看下。

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(),先是判断当前的线程是否已经有Looper了,如果没有,那就创一个looper,确保当前线程只有一个looper。ThreadLocal可以给当前的线程保存一个对象,如下代码所示。这里,它保存的是looper对象。

public void set(T value) {
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values == null) {
            values = initializeValues(currentThread);
        }
        values.put(this, value);
    }


我们再看看Looper的构造方法。

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


可以看到,looper里创建了一个messagequeue,并绑定了当前的线程。也就是说looper和thread已经是一对一对应了。

然后我们看看new 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构造方法里,FIND_POTENTIAL_LEAKS是false,所有那段代码直接忽略。然后可以看到,它执行了
mLooper = Looper.myLooper();


这个做了什么呢,让我们看看。

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


可以看到,它获取了当前thread保存的looper。

然后继续往下,拿到了looper创建的messagequeue。至此,handler已经成功获取到当前线程的looper。

接下来是执行
Looper.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();
        }
    }


在loop()里,先是获取当前线程保存的looper,从而得到messagequeue,然后从这个queue里试图获取下一条message。如果没有,则阻塞。从这里可以得出,new Handler()必须在Looper.loop()调用之前,否则,就执行不到了,因为一直阻塞在那里。

然后如果handler发送一条消息时,就会继续执行下去,并通过
msg.target.dispatchMessage(msg);
发送出去。发送给谁?其实是发送给原先的handler。

分析到这里,其实关于初始化工作已经差不多结束了,也就这么了了几行,现在就差发送消息过来。那么我们来看看创建消息和发送消息。

怎么创建消息呢?看看Message就知道。先贴个Message里的获取msg的方法。




可以看到一堆obtain,除了第一个obtain()之外,其余obtain(…)里第一句都是
Message m = obtain();
。既然你这么重要,那就进去看看呗。

/**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     */
    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }


可以看到,如果之前message有创建过,那就继续使用它,就不用new了,如果没有,那就新建一个。sPool就是前一个message。这部分在Looper.loop()里的最后一句可以看到
msg.recycleUnchecked();
看源码~

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++;
            }
        }
    }


看到了吧,先是重置所有参数,然后sPool = this;就把这个msg保存在sPool了。

然后我们随便看个参数多点的obtain()

public static Message obtain(Handler h, int what) {
        Message m = obtain();
        m.target = h;
        m.what = what;

        return m;
    }


就是这里了,m.target = h,这个h就是目标handler了。这样一来,创建了message,并关联了handler。当然,创建message还可以在handler里。看下Handler.obtainMessage()

public final Message obtainMessage()
    {
        return Message.obtain(this);
    }


这样就直接和这个handler关联了。

至此,创建message说完了。接着讲发送message。

就知道,又是一堆。




这里,我们就找个最简单的
sendEmptyMessage(int what)
看看。

public final boolean sendEmptyMessage(int what)
    {
        return sendEmptyMessageDelayed(what, 0);
    }


再进去~

public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }


这里,创建了一个message,赋了个what参数。继续进去

public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }


再进~

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);
    }


这里,获取到messagequeue,并传到下一层。继续往下!

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


吗呀~终于到了!请看这里,msg.target = this;这句话立马就把这条message的target指定给自己了。所以就像先前总结的,handler发送消息,其实最后还是给自己处理。

忘了?那么我们继续往下说。现在创建好了消息,并发送好了消息,接着又回到原先的Looper.loop()了,这货还在那干等着呢。

然后他欣喜地从
Message msg = queue.next();
里获得这个msg,并
msg.target.dispatchMessage(msg);


好,接着进去看。

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


从上面三个handler打头的看来,这里就是分发消息给其中一个处理了。

有两个干货:handlerCallback和handlerMessage。

我们分别来看下。

先handleCallback吧~

private static void handleCallback(Message message) {
        message.callback.run();
    }


可以看到,直接执行message里的callback参数的run();那callback是啥玩样儿?点下一看,原来是
Runnable callback;
也就是说,如果message初始化里包含了callback,那就先执行callback;

那么怎么用呢?最经典的做法是Handler.post(Runnable r)

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;
    }


好了,再看handleMessage(),这个应该很熟吧。

public void handleMessage(Message msg) {
    }


看到这里是空的,没事,我们的做法是实现它。

到此,初始化、创建message、发送message、处理message都已经讲完了。这样一步步下来是不是很清晰。那么我们再来炒下冷饭,看下之前总结的部分。

1. Looper

Looper的作用是绑定当前所在的thread,并创建一个MessageQueue,不断循环从MessageQueue读取消息,然后消息交给target(Handler)处理。

2. Handler

Handler的作用是获取当前所在线程的looper和其MessageQueue,并可发送消息到queue,然后再因message target指向其本身,辗转反侧回到自己这里进行处理,处理过程在当前线程中执行。

3. Message

Message是个载体,用来存储数据和指定target Handler。然后可以发送给那个Handler。

上述三个需要一个关键先生,那就是thread。thread是它们赖以生存的环境。没有thread就无法创建looper,没有thread,就无法执行handler的消息处理。

有了上面这些基础,再来看handlerThread,实在是so easy!

我们直接上代码,只贴部分代码。

public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;

    ......

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }
    ......
    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }

        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }
    ......
    public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }
    ......
}


HandlerThread继承Thread,从名字也能猜到啦。然后里面有个looper
Looper mLooper;


再看关键的run();

@Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }


有没有看到很熟悉的部分。没错,先是Looper.prepare(),接着中间有些处理,然后Looper.loop();

咦~怎么没有handler,别急,不是有个getLooper()嘛。这个getLooper()是获取HandlerThread里的looper。那咋整啊?可以直接赋给handler嘛,具体比方说可以用这个

public Handler(Looper looper, Callback callback) {
        this(looper, callback, false);
    }


这样不就有了吗?接下去,就和上面handler、looper、message的流程一样了。

再来炒个饭~~蛋炒饭~#%$

HandlerThread相当于是Thread和Looper的合体。HandlerThread本身继承自Thread,并自己内部创建Looper与其关联,创建了looper就创建了MessageQueue。然后我们可以创建Handler与HandlerThread创建的Looper进行关联。

到此,这四个娘们都已经被我拨了外衣给你们看了一通。过瘾吧,啥?不过瘾?那你自己剥去吧。

下次,搞几个典型的案例玩玩~

案例1:

Android使用Handler实现线程池的效果,实现照片墙应用
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: