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

来源:互联网 发布:怎么配置linux ip 编辑:程序博客网 时间:2024/05/21 14:03

本文一点一点的把与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

0 0
原创粉丝点击