Android之解读Handler

1.概述

Android中我们通常会把耗时操作放在子线程中,然后通过Handler来发送消息到主线程进行UI更新,本文通过探究源码来分析Handler背后的原理是什么,本篇主要涉及Message、MessageQueue、Looper、Handler这四个类的详细分析。

2.Message

Message类是个final类不能被继承,用作封装数据的容器,是链表结构,可以被发送给 Handler

  • **主要属性
public int what; //用户定义消息代码以便收件人可以识别这是哪一个Message
public int arg1; //如果只是想向message内放一些整数值,可以使用arg1和arg2来代替setData方法
public int arg2; 
public Object obj; //发送给接收器的任意对象,在使用 Messenger 跨进程传递消息时,通常使用它传递给接收者,在其他场景下我们一般使用 setData() 方法
/*package*/ static final int FLAG_IN_USE = 1 << 0; //标识消息是否在被使用
/*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1; //标识是否是异步消息
/*package*/ Bundle data;  //setData()用到的Bundle
/*package*/ Handler target; // 与消息关联的Handler
/*package*/ Runnable callback;  //处理消息的回调 
/*package*/ Message next; // 有时以链表的形式关联后一个消息
private static final Object sPoolSync = new Object();
private static Message sPool; //消息池
private static int sPoolSize = 0;

从这些属性中可以看出:

  • Message.what 用来标识干什么
  • 可以存储Bundle对象
  • Message持有Handler的引用,将Handler引用赋值给target
  • Message内部有一个消息池
获取Message
//构造方法
public Message() {
}

Message的构造方法是个空方法,官方更推荐通过Message.obtain()来获取一个Message,我们也经常使用Handler.obtainMessage()来获取消息,该方法内部也是调用Message.obtain()

//Handler 的obtainMessage()
public final Message obtainMessage(){
        return Message.obtain(this);
}

Message.obtain()源码如下:

    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // 清除在使用的标识
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

如果消息池中有消息的话就取出,设置标识未使用,消息的next属性设为null,将消息池指向该消息的下一个,如果没有的话就调用构造函数。Message.obtain()的其它重载方法都是在调用了该方法获取到Message之后,为其属性赋值。例如下面这个重载方法:

    public static Message obtain(Handler h, int what, int arg1, int arg2) {
        Message m = obtain();
        m.target = h;
        m.what = what;
        m.arg1 = arg1;
        m.arg2 = arg2;

        return m;
    }

既然消息是从消息池获取的,那么消息池的消息从哪来的呢?是消息被回收时放入的,消息回收时调用recycleUnchecked():

void recycleUnchecked() {
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = -1;
        when = 0;
        target = null;
        callback = null;
        data = null;

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

将消息的数据清除之后,这个消息加入了回收消息的链表中。

3.MessageQueue

MessageQueue管理着消息列表,消息由Handler来发送到MessageQueue,由Looper循环的取出

  • 主要属性
private final boolean mQuitAllowed;//表示MessageQueue是否允许退出
private long mPtr; //mPtr是native代码相关的
Message mMessages; //表示消息队列的头Head
private boolean mBlocked; //next()调用是否被阻塞

MessageQueue持有消息列表的头,是一个单链表的结构

  • 构造方法
    MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed; //true允许退出
        mPtr = nativeInit();
    }

MessageQueue调用native方法来进行初始化,该方法通常由Looper.prepare()调用

  • 消息入队:MessageQueue.enqueueMessage()
    当Handler发送消息到MessageQueue时,由该队列的enqueueMessage()方法来负责把Message插入到队列中
    boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) { // Message 必须关联Handler
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {  //如果该Message 已经在处理中,则抛出异常
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
            if (mQuitting) { // 如果队列正在退出时有消息入队,将该消息回收,并返回入队失败
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }

            msg.markInUse();
            msg.when = when;
            Message p = mMessages; // 临时变量p指向队列头
            boolean needWake;
            if (p == null || when == 0 || when < p.when) { // 插入的这个message应该在第一个位置也就是队首
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }
            if (needWake) {
                nativeWake(mPtr); //激活消息队列去获取下一个消息
            }
        }
        return true;
    }

Message入队时先判断是否要插入队首,如果不是的话则按照时间顺序插入到某个合适的位置。

  • 消息出队: MessageQueue.next()
    Message next() {
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            ... //省略代码

            nativePollOnce(ptr, nextPollTimeoutMillis); //等待被激活,然后从消息队列中获取消息
            synchronized (this) {
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;  //msg指向队首
                if (msg != null && msg.target == null) {  // 队首是消息屏障
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        // 下一个消息没有到要处理的时机则设置激活等待时间
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // 取得一个消息
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }
                 ... //省略部分无关代码
        }
    }

MessageQueue开启一个循环来取消息,队列被激活之后,首先判断队首是不是消息屏障,如果是则跳过所有的同步消息,查找最先要处理的异步消息。如果第一个待处理的消息还没有到要处理的时机则设置激活等待时间;否则这个消息就是需要处理的消息,将该消息设置为 inuse,并将队列设置为非 blocked 状态,然后返回该消息。

4.Looper

用于为Thread运行消息循环的类,Thread默认没有消息循环的Looper,需要在Thread中调用Looper.prepare()来创建一个Looper,然后调用Looper.loop()来执行消息循环。

  • 主要属性
static final ThreadLocal sThreadLocal = new ThreadLocal();
private static Looper sMainLooper;  // 主线程中的Looper
final MessageQueue mQueue; // 关联的消息队列
final Thread mThread; //Looper所在的线程

从属性中可以看到Looper中持有一个消息队列,就可以调用MessageQueue的相关方法。

  • 构造函数
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed); 
        mThread = Thread.currentThread();
    }

构造函数就两行代码,新建了一个MessageQueue,把Looper所在当前线程赋值给mThread属性,但是这个方法是private,外部不能通过调用它来构造一个Looper,该方法在Looper.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));
    }

prepare()方法通过构造方法生一个Looper然后把它保存在了ThreadLocal中,不过在这之前先判断了ThreadLoacl中是否保存了一个Looper,如果有的话就会抛出异常,这说明一个Thread只会有一个Looper与之关联,那么来看一下ThreadLocal是什么

  • ThreadLocal
    ThreadLocal的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用,在本线程内随时随地可取,隔离其他线程。所以Looper通过ThreadLocal可以在线程中存取,线程取得与之关联的Looper之后,就可以调用Looper.loop()循环取出消息,并且每个线程的Looper是独立的,不相关的。
  • Looper.loop() 循环取出消息并处理
public static void loop() {
    final Looper me = myLooper();
    if (me == null) {    //当前线程必须创建 Looper 才可以执行
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;

    //底层对 IPC 标识的处理,不用关心 
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    for (;;) {    //无限循环模式
        Message msg = queue.next(); //从消息队列中读取消息,可能会阻塞
        if (msg == null) {    //当消息队列中没有消息时就会返回,不过这只发生在 queue 退出的时候
            return;
        }

        //...
        try {
            msg.target.dispatchMessage(msg);    //调用消息关联的 Handler 处理消息
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        //...
        msg.recycleUnchecked();    //标记这个消息被回收
    }
}

loop()方法首先取到Looper关联的MessageQueue,开启一个无限循环调用MessageQueue.next()来不断的取出消息,取出消息之后调用msg.target.dispatchMessage(msg);前面介绍Message时说到Message的target属性就是Handler,这里就将消息交给了Handler来处理。要注意将消息交给Handler处理是在Loop()中,所以Looper.loop()被调用的线程就是Handler处理消息的线程。例如通过Handler更新UI时,在子线程调用Handler.sendMessage()发送消息,由于UI线程的Looper.loop()已经由系统在UI线程调用,所以Handler处理消息也在主线程,总结来说:Looper.loop()在哪个线程调用的,Handler就切换到哪个线程处理消息

5.Handler

Handler允许你发送和处理一个Message和Runnable对象,每个Handler实例都和一个单独的线程和线程的MessageQueue相关联。

  • 主要属性
final Looper mLooper; 
final MessageQueue mQueue;
final Callback mCallback;
final boolean mAsynchronous;
IMessenger mMessenger;

Callback 是Handler内部的一个接口,它可以作为构造函数的参数用于新建 Handler。

public interface Callback {
        public boolean handleMessage(Message msg);
}
//Handler的一个构造函数
public Handler(Callback callback) {
        this(callback, false);
    }
// 例如:
Handler mHandler = new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        //这里处理消息
        return false;
    }
});

Handler有许多重载的构造函数,他们的作用都是给上面的这几个属性赋值,要注意的是mLooper在赋值时由两种方式:
1.构造Handler时不传递Looper对象,Looper从当前线程获取,当前线程不是UI线程时,需要先调用Looper.prepare()
2..构造Handler时构造参数传递Looper对象,Handler就与传递的Looper和Looper所在的线程相关联
MessageQueue 由Looper.mQueue俩获取,所以构造Handler时就已经与所在线程、Looper、MessageQueue向关联了。

  • Handler发送消息原理
    我们用Handler发送消息时通常使用 handler.sendMessage(message),下面我们来看这个方法及其类似方法是怎么实现的:
public final boolean sendMessage(Message msg) {
        return sendMessageDelayed(msg, 0);
    }

public final boolean sendEmptyMessage(int what) {
        return sendEmptyMessageDelayed(what, 0);
    }

 public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }

 public final boolean sendMessageDelayed(Message msg, long delayMillis) {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

 public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageAtTime(msg, uptimeMillis);
    }

这几个发送Message的方法最后都调用了sendMessageAtTime()方法,这个方法源码如下:

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

参数msg即是我们要发送的Message,uptimeMillis参数则表示发送消息的时间,它的值等于自系统开机到当前时间的毫秒数再加上延迟时间,方法中的mQueue是Handler初始化的时候赋值的,然后把这三个参数传入enqueueMessage()方法:

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

把Message的target设置为当前Handler,最后queue.enqueueMessage(msg, uptimeMillis)把消息插入消息队列中,至此消息的发送就完成了

  • Handler接受消息并处理
    既然Message进入了消息队列,就有出队的方法,出队操作是由Looper.loop()来执行的,loop()方法开启一个无限循环了从MessageQueue去取出Message,然后调用Message.target.dispatchMessage(),Message.target就是Handler,所以呢走到了Handler的dispatchMessage():
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

可以看到,Handler 在处理消息时会进行判断:

  1. msg.callback 不为空
    如果Message的callback属性不为空的话就直接调用Runnable的run()方法。
  2. mCallback 不为空
    如果使用Callback构造了Handler,则会进入到Callback接口的handleMessage(),该方法返回true的话就不会往下走了。
  3. 最后就调用 Handler.handleMessage() 方法,例如:
        Handler handler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                //处理Message
            }
        };

至此Handler就可以获取到Message并处理了

  • Handler.post()系列方法
public final boolean post(Runnable r){
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

public final boolean postAtTime(Runnable r, long uptimeMillis){
        return sendMessageAtTime(getPostMessage(r), uptimeMillis);
    }

public final boolean postAtTime(Runnable r, Object token, long uptimeMillis) {
        return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
    }

public final boolean postDelayed(Runnable r, long delayMillis) {
        return sendMessageDelayed(getPostMessage(r), delayMillis);
    }

post()系列方法和上面的sendMessage()系列方法一样,最终都是调用sendMessageAtTime(),不同的地方在于参数传了一个getPostMessage(r),我们来看一下这个方法

    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

该方法就是构造了一个Message,并把参数Runnable设置为Message的callback属性,sendMessageAtTime()接下里就和上面的流程一样了

总结

  • Handler的构造时会关联一个线程,并发送消息到该线程关联的MessageQueue中
  • Thread默认没有Looper,需要调用Looper.prepare()来初始化Looper,且只能初始化一次
  • 每个looper会维护一个MessageQueue,Looper负责循环的从MessageQueue取消息并交给Handler处理

Handler异步处理消息流程如下:


图片出自:http://blog.csdn.net/u011240877/article/details/72892321

你可能感兴趣的:(Android之解读Handler)