Android消息机制源码阅读

前言

Android客户端开发和消息机制最常见的就是网络请求数据更新到UI线程,用户在子线程中通过网络请求获取数据,由于Android不支持直接在子线程中修改UI控件,需要把数据转交到UI线程中再更新控件界面。这个过程通常要借助Handler类调用post/sendMessage系列方法将操作提交到消息队列中,主线程不断取消息队列里的消息并执行回调。为了更好地理解整个通信过程,这里查看底层的实现源码,首先从Handler类开始。

源码分析

Handler的post/sendMessage系列方法负责将消息放入消息队列中查看这些方法的实现源码。

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

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

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

可以发现所有的调用最终都会调用sendMessageAtTime()方法上去,在这个方法里将Message对象入队列到MessageQueue中,也就是消息队列中。

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        return false;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
}

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    // 注意msg.target的赋值操作
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

在查看消息队列的实现之前先查看Message类,作为Android消息机制的重要参与者,消息对象封装了用户提交的数据和与用户获取消息对象的接口。

public final class Message implements Parcelable {
    // 用来指定消息的类型
    public int what;

    // 用户自定义的参数
    public int arg1;
    public int arg2;
    public Object obj;

    // 消息对应的Messenger,这个属性用在Messenger多进程通信上
    public Messenger replyTo;

    // 消息标识,消息是异步消息,是否在使用中
    /*package*/ int flags;
    // 消息发送事件
    /*package*/ long when;
    // Bundle类型的用户数据
    /*package*/ Bundle data;
    // 发送消息的Handler
    /*package*/ Handler target;
    // 消息被接收到后需要执行的回调对象
    /*package*/ Runnable callback;
}

上面的消息字段就是消息传递内容时常用的字段,特别需要注意的是Handler类型的target对象,它会在后面的消息队列中使用到。查看后面的几个字段采用了连表机制实现消息对象的缓存。

// 下一个消息引用
/*package*/ Message next;

// 缓存消息对象列表同步器
private static final Object sPoolSync = new Object();
// 缓存消息头部
private static Message sPool;
// 缓存消息数量
private static int sPoolSize = 0;
// 最多缓存消息个数
private static final int MAX_POOL_SIZE = 50;

// 获取缓存消息接口,如果没有缓存就new个新对象
public static Message obtain() {
    synchronized (sPoolSync) {
        if (sPool != null) {
            Message m = sPool;
            sPool = m.next;
            m.next = null;
            m.flags = 0; // clear in-use flag
            sPoolSize--;
            return m;
        }
    }
    return new Message();
}

// 回收被使用的消息对象到消息缓存池中
public void recycle() {
    if (isInUse()) {
        if (gCheckRecycle) {
            throw new IllegalStateException("This message cannot be recycled because it "
                    + "is still in use.");
        }
        return;
    }
    recycleUnchecked();
}

上面的代码是FlyWeight的设计模式,Message对象在Android应用中需要频繁的创建和销毁,这样就会导致不停地出现gc操作。为了避免不必要的创建和销毁操作,就将Message缓存在内存中,当用户需要使用时先判断内存有无缓存,没有再新建,有则复用之前回收的Message对象。

接下来再讨论MessageQueue的来源,会发现消息队列是从Looper对象中的一个成员对象,也就是说Looper对象创建了消息队列。

public Handler(Looper looper, Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

查看Looper对象的实现源码,发现MessageQueue正式Looper对象创建地。不过Looper的构造函数居然是private私有类型,那么只能在Looper内部构造它自己,别的地方无法创建它的实例。

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

查看Looper代码中调用了构造函数的地方,发现主要有三个方法构造了Looper对象,并且这些Looper还被存放到了sTreadLocal线程本地量对象中。

// 存放Looper的线程本地量
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

// 创建Looper对象并且放到线程本地量中
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));
}

// 创建主线程也就是UI线程的Looper并且放到线程本地量和sMainLooper中
public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

public static Looper getMainLooper() {
    synchronized (Looper.class) {
        return sMainLooper;
    }
}

上面的创建方法prepared既可以在主线程中调用,也可以在子线程中调用,如果在同一个线程中调用两次prepare()这时因为在线程本地量中已经有一个Looper对象,检查后会抛出每个线程只能创建一个Looper的运行时异常。主线程的Looper创建有时在哪里调用的呢?在Android的ApplicationThread类中有一个main方法,Java开发都知道main方法是Java程序的入口,查看其实现代码。

public static void main(String[] args) {
    ...
    Process.setArgV0("<pre-initialized>");

    Looper.prepareMainLooper();

    ActivityThread thread = new ActivityThread();
    thread.attach(false);

    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }
    ...
    Looper.loop();

    throw new RuntimeException("Main thread loop unexpectedly exited");
}

去掉多余的代码可以发现在Andorid应用启动的时候就创建了主线程的Looper对象,之后又调用了Looper.loop()方法,之后跑出了一个主线程循环意外退出的运行时异常,如果这个异常抛出了那么Android应用也退出,所以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;

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

        // 取到消息后调用msg.target.dispatchMessage方法
        final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
        final long end;
        try {
            msg.target.dispatchMessage(msg);
            end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        ...
        // 回收消息对象到消息缓存中
        msg.recycleUnchecked();
    }
}

msg.target.dispatchMessage(msg);前面的Message源码分析的时候提到了Handler类型的target,这里其实就是回调了Handler.dispatchMessage(msg);方法,用户通常在dispatchMessage(msg)调用的handleMessage();方法里会针对不同的消息类型做不同的操作。

Handler和Looper的源码都已经分析完成了,接下来开始查看保存发送的Message对象的MessageQueue实现。sendMessageAtTime是调用了enqueueMessage方法将消息加入消息队列。

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

查看MessageQueue.enqueueMessage();方法会正常会将用户传递的消息放入消息队列中去。注意入队列的操作是放在synchronized(this);代码块中执行的,这时因为入队操作可以在子线程中,例如前面提到的网络请求数据的线程可以将操作入队到主线程消息队列。

boolean enqueueMessage(Message msg, long when) {
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }
    if (msg.isInUse()) {
        throw new IllegalStateException(msg + " This message is already in use.");
    }

    sy {
        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;
        boolean needWake;
        if (p == null || when == 0 || when < p.when) {
            // New head, wake up the event queue if blocked.
            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;
        }

        // We can assume mPtr != 0 because mQuitting is false.
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

在这里入队之后前面的Looper死循环会在queue.next();操作读取队列里的新消息。

Message next() {
    ...
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }

        nativePollOnce(ptr, nextPollTimeoutMillis);

        synchronized (this) {
            // Try to retrieve the next message.  Return if found.
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            if (msg != null && msg.target == null) {
                // Stalled by a barrier.  Find the next asynchronous message in the queue.
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                if (now < msg.when) {
                    // Next message is not ready.  Set a timeout to wake up when it is ready.
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // Got a message.
                    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;
            }

            // Process the quit message now that all pending messages have been handled.
            if (mQuitting) {
                dispose();
                return null;
            }
        ....
    }
}

同样在消息出队列的时候也是用synchronized(this);代码块,这是因为底层的单链表消息队列在多线程中反问,需要使用同步处理,否则多线程访问会出异常。

总结

ActivityThread.main()方法作为Android应用的入口,在里面调用Looper.preparedMainLooper()创建了主线程Looper,同时调用了Looper.loop()开始死循环读取MessageQueue消息队列里的消息;用户通过Handler.post/send发送消息到MessageQueue队列中,当Looper监测到有新的消息入队列就会从MessageQueue.next()方法返回该消息并且在主线程中执行msg.target.dispatchMessage();最终调用用户自定义的Handler.handlerMessage()方法逻辑,也就是通过消息机制实现子线程和主线程的通信。

猜你喜欢

转载自blog.csdn.net/xingzhong128/article/details/80019421