Android handler机制浅析

来源:互联网 发布:c语言sleep函数头文件 编辑:程序博客网 时间:2024/06/05 17:29

在安卓中,Handler负责消息的传递,它提供了一套在线程间传递消息的机制。Handler与looper、message、messageQueue一起,共同构成了安卓的消息发送框架。初学安卓的朋友可能对安卓的线程模型感到困惑,也会感到有很多限制,UI线程既不能等待,又不能做耗时操作,非UI线程可以做耗时操作,却不能改变UI。但这些问题却可以通过handler——线程间的消息传递来解决。那么handler内部是怎么实现的呢?它的实现原理是什么?让我们一起来探个究竟。

下面是Handler的一个基础用法。我们首先在主线程里定义了一个Handler,然后新启动一个线程,在该线程里通过该handler的post方法可以向主线程发送消息。在post方法下的runnable会运行在主线程中,这样就实现了“在非UI线程更新UI”的操作。

   private Handler mHandler;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        mHandler = new Handler();        new Thread(new Runnable() {            @Override            public void run() {                mHandler.post(new Runnable() {                    @Override                    public void run() {                        //在这里可以更新UI                        ...                    }                });            }        }).start();    }

在讲Handler机制之前,我们首先需要讲一下与handler有关的几个类:ThreadLocal、MessageQueue、Message和Looper。ThreadLocal的作用为提供一个只与单个线程有关的变量,在我的博客ThreadLocal源码浅析一文中对ThreadLocal进行了详细讲解,有兴趣的朋友可以看看。MessageQueue则是一个消息队列,以队列的形式存储着handler发送的消息(Message)。而Looper则是消息机制的核心类,它封装了ThreadLocal,MessageQueue,负责从消息队列中轮询、获取消息并发送等核心操作。它的两个核心方法prepare()和loop()方法也是本篇博客的重点。下面我们来看一下Looper是怎么实现的。

首先来看下Looper类的几个重要属性:

public final 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>();    private static Looper sMainLooper;  // guarded by Looper.class    final MessageQueue mQueue;    final Thread mThread;    private Printer mLogging;    ...}

Looper定义了几个比较重要的属性:
sThreadLocal
sMainLooper
mQueue
sThreadLocal用于存储每个线程的looper,即使得每一个线程都有一份自己的looper(如果设置的话),sMainLooper则为主线程的looper,mQueue则为Looper所包含的消息队列。要注意的是,sThreadLocal和sMainLooper都被设为static的,即在一个进程中,系统只存在一个mainLooper,sThreadLocal也被各个线程共享,只不过每个线程在sThreadLocal中取到的值不同(见ThreadLocal源码浅析)。

Looper还有两个非常重要方法:prepare()和loop()。先来看下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的新实例放入threadLocal中,也就是说,当前线程和一个Looper绑定了,我们可以理解为该线程变成了“looper线程”,这个线程拥有了自己单独的looper实例,也就意味着拥有了单独的messageQueue、Printer等。此外,一个线程只能绑定一次Looper,再次绑定会抛出RuntimeException异常。

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();        }    }
    /**     * Return the Looper object associated with the current thread.  Returns     * null if the calling thread is not associated with a Looper.     */    public static Looper myLooper() {        return sThreadLocal.get();    }

Loop方法也比较清晰:首先,loop方法会调用myLooper()方法从ThreadLocal里返回当前线程所绑定的Looper,接着,得到这个Looper的MessageQueue,然后,loop()方法会定义一个无限循环,不断地从这个MessageQueue里获取消息( Message msg = queue.next(); ),并发送给对应的handler(msg.target.dispatchMessage(msg);)。所以,我们在使用Looper的时候,首先要调用它的prepare()方法,将当前线程变成“looper线程”,再调用looper.loop()方法循环获取消息。
举个小例子:

    new Thread("lzq thread") {            @Override            public void run() {                Looper.prepare();//将本线程变为Looper线程                Handler myHandler = new Handler() {                    @Override                    public void handleMessage(Message msg) {                        Log.v("lzq", Thread.currentThread().getName());                    }                };                myHandler.sendEmptyMessage(0);                Looper.loop();//由于是无限循环,要在线程最后调用            }        }.start();

可以看到,打印出的消息为lzq thread,证明handler运行在了子线程。幸运的是,在安卓的主线程中已经为我们做好了这些事情,在操作主线程的时候我们只要调用handler发送和接收消息就可以了。

我们再来看看Handler是怎样向MessageQueue里push消息的。首先看下Handler的构造方法:
Handler的构造方法很多,这里例举出两个:

 public Handler(boolean async) {        this(null, async);    }
 public Handler(Callback callback, boolean async) {        ...        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内部包含了Looper的实例mLooper,和MessageQueue mQueue。在默认情况下,mLooper被实例化成当前线程绑定的looper,而mQueue则为该looper内部的MessageQueue。当我们采用handler调用sendMessage或post的时候:

    public final boolean sendMessage(Message msg)    {        return sendMessageDelayed(msg, 0);    }

sendMessage和post方法都会调用一系列的方法,但最终会转到这个方法:

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

通过enqueueMessage方法,待发送的Message会被放入到handler对应的looper的MessageQueue中,进而等待执行了。

我们再回过头来看最开始的例子,handler定义在主线程中,也就是说,该handler关联了主线程的looper,这样的话,当其它线程调用这个handler的post()或sendMessage()方法时,本质上是将message放入主线程looper的messageQueue中,再适时进行执行,这样就实现了“在非UI线程更新UI”的操作。因为本质上还是通过主线程更新UI,所以在该handler中不能做耗时的操作。

总结:
Handler与looper、message、messageQueue一起,共同构成了安卓的消息发送框架。先从looper说起,looper有两个核心方法:prepare()和loop()。prepare方法会将一个looper实例放在当前线程的threadlocal中,并使looper维护一个唯一的MessageQueue,使当前线程变成looper线程,loop方法则是构建一个无线循环,循环遍历messageQueue,将messageQueue中的message取出并发送,最后会调用对应handler的handleMessage方法处理。总之,looper负责绑定线程,循环处理消息。而handler负责接收并传递消息。handler内部需要绑定一个looper。我们要做的就是调用handler.sendmessage方法,他会将message发送给handler对应的looper下面的Messagequeue中,再通过looper.loop方法进行调用处理。我们再调用handler.handleMessage接收。这样就实现了线程间的通信。

0 0
原创粉丝点击