android学习13#--Handler消息传递机制

本文一点一点的把与handler相关的知识点都引了出来,尽最大努力把这个机制讲清楚。

为什么android要求子线程通过Handler来更新UI

我们先来看看官网[https://developer.android.com/training/multiple-threads/communicate-ui.html#Handler]的这段文字:
Every app has its own special thread that runs UI objects such as View objects; this thread is called the UI thread. Only objects running on the UI thread have access to other objects on that thread. Because tasks that you run on a thread from a thread pool aren’t running on your UI thread, they don’t have access to UI objects. To move data from a background thread to the UI thread, use a Handler that’s running on the UI thread.
总的意思是:app启动总是会创建一个单独主线程UI线程,约定组件操作只能再UI线程中完成,其它线程需要操作组件时,需借助Handler来完成。

Handler功能

  1. [https://developer.android.com/reference/android/os/Handler.html]上的文字:
    A Handler allows you to send and process Message and Runnable objects associated with a thread’s 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.
    基本意思:Handler允许你发送和处理与线程的MessageQueue有关的message和runnable对象,一个handler对象只能和一个thread有关,每一个handler的创建一定会绑定一个thread或message queue;从那时起,创建handler的线程将会发送message和runnable到message queue,而主线程会从message queue中取出来处理。
  2. 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.
    Handler主要有两个用途:(1) message和runnable会在将来的某个时间点执行;(2) 需要再另一个线程中添加入队动作,不是本线程。
  3. Handler是线程管理框架的一部分,主要是接收子线程发送的数据,并用此数据配合主线程更新UI,用来跟UI主线程交互用。
    • 比如在子线程处理一些耗时的操作,然后子线程用handler发送一个message给主线程UI线程的handler来接收、处理该消息更新UI,这样以避免直接在UI主线程中处理事务导致影响UI主线程的其他处理工作,Android提供了Handler作为主线程和子线程的纽带;
    • 也可以将handler对象传给其他进程,以便在其他进程中通过handler给你发送事件;
    • 还可以通过handler的延时发送message,可以延时处理一些事务的处理。

创建Handle

  1. Handler():
    Default constructor associates this handler with the Looper for the current thread.
  2. Handler(Handler.Callback callback):
    Constructor associates this handler with the Looper for the current thread and takes a callback interface in which you can handle messages.
  3. Handler(Looper looper):
    Use the provided Looper instead of the default one.
  4. Handler(Looper looper, Handler.Callback callback):
    Use the provided Looper instead of the default one and take a callback interface in which to handle messages.

创建looper

上面的4个构造方法中,会发现每一个构造方法都会有一个looper类。先来说说looper类
API中提到:This class contains the code required to set up and manage an event loop based on MessageQueue
意思是Looper类包含了需要建立和管理一个基于MessageQueue的事件代码

[https://developer.android.com/reference/android/os/Looper.html]上讲:
Class used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, call prepare() in the thread that is to run the loop, and then loop() to have it process messages until the loop is stopped.
Looper类是用于处理线程的消息队列的这么一个类。线程默认情况下不带有消息循环;在线程中调用prepare()方法创造looper对象,然后调用loop()方法启动消息处理,直到loop结束。
先来看看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) {//这个判断证了一个线程中只有一个Looper实例
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

上面的prepare()方法保证了每一个线程最多只有一个looper对象,注意prepare()是一个静态函数,再来看看looper构造方法源码:

    /**
     * Return the {@link MessageQueue} object associated with the current
     * thread.  This must be called from a thread running a Looper, or a
     * NullPointerException will be thrown.
     */
    public static @NonNull MessageQueue myQueue() {
        return myLooper().mQueue;
    }

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

这里会创建一个MessageQueue对象。因此在初始化Looper对象时,会创建一个与之关联的MessageQueue,这个MessageQueue负责管理消息。
再来看看启动消息管理的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()方法是采用一个死循环的方式遍历MessageQueue中的message,调用调用msg.target.dispatchMessage(msg)将消息分发给对应的Handler进行处理。

再回到Handler构造函数

我们发现第一个跟第二个构造函数并没有传递Looper,查看源码,这两个构造函数其实调用的是下面这个函数,源码如下:

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

你会发现最终会调用mLooper = Looper.myLooper();来获取当前线程绑定的Looper对象。同时通过这个Looper对象获取对应的MessageQueue对象。
第三个跟第四个构造函数实际调用的是如下函数:

    public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

looper对象是外面传递进来的,这个不难理解。
再来看看第二个和第四个构造函数有一个参数Callback callback。查看Callback对象,发现它是Handler的内部接口,需要实现内部的handleMessage()方法,接口源码如下:

    /**
     * Callback interface you can use when instantiating a Handler to avoid
     * having to implement your own subclass of Handler.
     *
     * @param msg A {@link android.os.Message Message} object
     * @return True if no further handling is desired
     */
    public interface Callback {
        public boolean handleMessage(Message msg);
    }

很明显这个接口是用来处理消息的方法,该方法通常需要被重写。重写有两个方法:
1. 采用回调的形式,即通过第二个或第四个初始化Handler对象。
2. 采用重写的方法,即通过第一个或第三个初始化Handler对象。
但是实际中具体是哪一种构造方法用得多,我也不是很清楚,先了解这些原理吧,后续实践的时候留意下。

Handler分发消息的一些方法

post(Runnable)
postAtTime(Runnable, long)
postDelayed(Runnable, long)
sendEmptyMessage(int)
sendMessage(Message)
sendMessageAtTime(Message, long)
sendMessageDelayed(Message, long)
查看源码最后都是调用了sendMessageDelayed()方法,sendMessageDelayed()调用的是sendMessageAtTime()方法,sendMessageAtTime()最终调用的是enqueueMessage();

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

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;//this就是当前的Handle对象
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

上面的几个函数实现逻辑:把当前的Handler作为msg的target对象,通过Handler的mQueue对象的enqueueMessage()方法将消息放入MessageQueue的消息队列中。

总结

到了这里,总结一下,前面我依次从Handler入手,引入了MessageQueue,由Handler构造函数引入Looper,再由Looper引入MessageQueue,然后又回到Handler构造函数,哈哈,这条路是不是很清晰。
1. Looper:每个线程只有一个looper,负责新建和管理MessageQueue,负责将message从MessageQueue中取出来分发给对应的Handler。
2. MessageQueue:由looper创建并管理。
3. Handler:把消息发送给Looper管理的MessageQueue,并负责处理Looper分发给他的消息。
再来梳理下线程中使用Handler的步骤:
1. 调用Looper的静态prepare()方法为当前线程创建一个looper对象,创建looper对象是,系统会自动创建一个与之配套的MessageQueue。
2. 当前线程有了looper对象和MessageQueue后,就可以创建Handler了,记得根据需求重写handleMessage()方法
3. 最后调用Looper的静态loop()函数启动Looper。

需要特别说明的是,我们的主线程UI Thread在app系统一启动的时候就会创建,同时也会创建一个looper对象,因此再主线程中,我们可直接创建Handler对象,而不需要按照上面的所说的先调用Looper.prepare()等后再创建Handler对象

引用:

http://blog.csdn.net/amazing7/article/details/51424038
http://www.cnblogs.com/angeldevil/p/3340644.html
http://blog.csdn.net/goodlixueyong/article/details/50831958
http://www.jb51.net/article/37465.htm
http://blog.csdn.net/army_jun/article/details/51729351
http://blog.csdn.net/heng615975867/article/details/9194219
http://blog.jobbole.com/73267/
http://www.cnblogs.com/colder/p/3791299.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章