Android handler机制浅析

在安卓中,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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值