Android Handler + Looper + Message理解

类似于iOS以及其他桌面平台的Runloop模型,Android也是这种处理方式。从原来看gtk/mfc开始,后来学习Linux编程中的IO模型和node.js,还有一些第三方类库libuv/libevent,为了简化多线程模型,基本都是采用这种方式解决。这样带来的好处太大了,省却了非常多的并发开销,充分利用CPU。刚接触Andorid开发,官方教程中说有三种在主线程中执行代码的方式,但最后其实都是一种,looper + handler + message实现的。做Android开发比iOS开发最好的一点是,随时可以查看源代码


Message

先从Messge对象说起。看的一本书中说Message对象是可以recycle的,通过static factory methods创建,这样可以减轻GC的压力。获取Message的话不是通过new,而是通过Message对象的obtain方法,这个方法和recycle方法配合可以实现Message对象重用。大量使用Message进行调用,并不会引起性能上的负担,这点尤其好。Message对象里面的属性很多,我个人觉得对粗略理解整个过程有用的属性是:

  • public int what;  // 这个属性比较有用,用于较为简单的int数字来进行简单的通信
  • Handler target;  // 这个是Handler对象,用于Message进行调用的时候使用,可以通过调用Handler的dispatchMessage方法将Message对象中进行callback,后面细讲这个方法
  • Runnable callback; // 这个是一个调用回掉方法,如果设置这个callback,这里面的run()方法中的代码将会在Handler的dispatchMessage方法中进行调用

根据我的理解,Message对象简单来说就是一个可复用的callback代码封装,用于在Handler与Looper之间进行传递回掉信息,用于looper循环中调用,这里面的callback代码会在looper中通过Handler被调用。


Handler

我认为Handler是整个looper机制的核心。如果要在主线程中执行代码,可以通过简单地调用View.post(Runnable ction)来进行调用,我一直很像知道内部是如何实现的,Android开发就是好,看看源代码就行,发现里面是把Runnable传递给Handler的post方法,post方法会把Runnable封装为一个Message对象,再放入Looper的MessageQueue中等待looper循环获取执行。

public boolean post(Runnable action) {
    final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            return attachInfo.mHandler.post(action);
        }
        // Assume that post will succeed later
        ViewRootImpl.getRunQueue().post(action);
        return true;
}

上面的代码就是View.post的代码,很明显,首先获取一个Handler对象,然后传递进入一个Runnable对象,如果该view没有AttachInfo的话,会转到另外一种方式进行调用,因为刚刚接触andriod开发,这个就细深入看了

public final boolean post(Runnable r)
{
       return  sendMessageDelayed(getPostMessage(r), 0);
}

上面这段代码是handler的中的post方法,其中getPostMessage方法将Runnable对象封装为一个Message对象,然后通过sendMessageDelayed进行传递,注意到上面这个方法的第二个参数为0,这个是延迟执行的意思。这个方法最终会调用到:

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

注意一下,这里会将handler对象的指针传递给Message的target成员,后面会使用这个指针。这里最主要的核心是queue,也就是looper的MessageQueue对象,这个对象管理所有的Message,并且这个对象线程安全,而且内部的处理可以进行延迟调用的处理。looper中获取要执行的Message,也是通过queue这个对象获取的,如果说Handler是Looper和Messge执行的中间对象,那MessageQueue就是数据结构中间对象。


上面主要讲的是将Message放入到MessageQueue中,等待looper执行,而Message执行调用代码也是通过Handler执行的,代码如下:

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


这里面,首先判断Message对象的callback即Runnable是否设置,如果设置的话,直接进行Runnable的run()方法。没有设置的话,则判断Handler对象的mCallback是否设置,如果设置的话,则进行回掉。如果都没有设置,则会调用自身的handleMessage方法,如果是Hanlder的子类,则可以覆盖这个方法进行处理。如果都没有情况下,则该message不会进行处理。


Looper

Looper是具体执行做代码执行的方法,里面在线程中进行for(;;)循环,通过MessageQueue获取Message对象,然后再进行回调。

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.recycle();
 }


上面的代码比较简单,下面说几个觉得需要注意的

1. msg.target.dispatchMessage(msg)这一行,是Message通过Handler对象进行回调,这样就把整个过程连接起来了。其实我一直在疑惑为什么不直接调用msg.callback.run()进行回调,而是要通过Handler进行dispatch,看了代码之后才明白,Handler本身也有个Callback接口对象,这个多一种情况能处理Message回调,可以处理一些只有what字段的简单的empty message,看过HanlderThread的Demo就是这样的。同时,我觉得这样可以进行大门功能分层,Message被限制为传递消息的对象,而Handler才是具体执行的对象,模块功能划分比较清晰。


2. 经同事提醒,觉得queue.next()方法应该重点提一下,这里如果queue为空的情况下,会发生等待。看了一下next()里面的代码,是使用一个nativePollOnce的JNI调用保持等待的,猜测里面应该是使用epoll之类的POSIX IO模型实现,这样可以节省大量的CPU资源。唤醒使用通过JNI调用nativeWake方法来进行的。


3. 最后一句msg.recycle()方法,这句就是实现Message对象重复使用的代码,在Android Pushing the limits书中有讲到


由于刚开始做Android,上面很多理解肯定是有偏差的,也没有仔细阅读代码进行学习,这个只是为了保证自己对Android开发的运行机制有个粗略的了解,以便遇到问题无法解决



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值