Android的消息机制源码分析

前言

Android系统不能在子线程中更新UI界面,因为Android的UI控件不是线程安全的。这种情况下我们可以在UI线程中创建一个Handler,子线程通过这个Handler发送Message到UI线程,通知UI线程更新UI界面。这是Android的消息机制一个非常典型的应用场景。在Android中,AsyncTask、IntentService等也都用到了Android的消息机制。通过源码分析Android的消息机制,我们可以深入了解一条Message从发送到最终被处理的完整过程。

基础知识

Android的消息机制是由Handler、MessageQueue和Looper组成的一整套消息发送、消息处理的消息系统。其中,MessageQueue用来存储Message,Looper用来为线程提供消息循环,Handler用来发送和处理Message。

在Android中,可以为每个线程创建一个独立的消息循环。这个功能可以通过一个ThreadLocal变量来实现。ThreadLocal变量可以为每个线程提供一个独立的对象副本,它们之间的操作是互不干扰的。通常,一个类中的ThreadLocal变量是private static类型的。我们可以通过ThreadLocal变量的get()方法来获取当前线程的对象副本,通过set()方法来设置当前线程的对象副本。

MessageQueue的工作原理

MessageQueue的作用是存储Message。我们可以通过Looper.myQueue()静态方法来获取当前线程的MessageQueue。Message并不是直接被添加到MessageQueue中的,而是通过与线程相关联的Handler对象来添加的。比如:Handler的sendMessage()、sendMessageDelayed()等方法。

对于MessageQueue,我们主要关注两个方法:添加一条Message的enqueueMessage()方法和获取下一条Message的next()方法。我们先来看添加一条Message的enqueueMessage()方法。

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

可以看到,添加到MessageQueue的Message的target不能为null,否则将抛出异常。Message的target其实是一个Handler对象,这条Message最终就是由这个Handler对象来处理的。我们通常是通过Handler对象的一系列obtainMessage()方法来获取一条Message。这种方式不仅高效地从全局的Message池中获取一条Message,而且它设置了这条Message的target为这个Handler对象。

同时,添加到MessageQueue的Message不能处于使用中的状态,否则也将抛出异常。一条新的Message当它被添加到MessageQueue中时,它就一直处于使用中的状态。接着往下看enqueueMessage()方法。

boolean enqueueMessage(Message msg, long when) {
    ...
    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;
        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 {
            // Inserted within the middle of the queue.  Usually we don't have to wake
            // up the event queue unless there is a barrier at the head of the queue
            // and the message is the earliest asynchronous message in the queue.
            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;
        }
        ...
    }
    return true;
}

如果mQuitting变量为true,即MessageQueue退出了,那么添加Message将失败。反之,Message将被添加到MessageQueue中。

首先,调用Message的markInUse()方法标记Message处于使用中的状态。然后,将Message添加到MessageQueue维护的mMessages链表中。这个链表按照Message添加进来时的when时间变量从小到大的顺序来存储Message。

接着,我们来看获取下一条Message的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;
            }
            ...
        }
        ...
    }
}

可以看到,next()方法是一个死循环,即默认情况下程序会阻塞在这里。

首先,从mMessages链表中取出第一条Message。如果Message不为null,并且达到了Message的when时间,那么就返回这条Message。当mQuitting变量为true,即MessageQueue退出时,next()方法返回null。

Looper的工作原理

Looper的作用是为一个线程提供一个消息循环。默认情况下,线程没有与它相关联的Looper。在线程中调用Looper.prepare()静态方法可以为该线程创建一个Looper。调用Looper.loop()静态方法可以启动该线程的消息循环。调用Looper.myLooper()静态方法可以获取该线程的Looper。

在Android中,是通过Handler对象来与Looper进行通信的。下面是一个实现Looper线程的典型例子:

class LooperThread extends Thread {
    public Handler mHandler;

    @Override
    public void run() {
        Looper.prepare();

        mHandler = new Handler() {

            @Override
            public void handleMessage(Message msg) {
                // process incoming messages here
            }
        };

        Looper.loop();
    }
}

我们先来看创建一个Looper的Looper.prepare()静态方法。

// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal sThreadLocal = new ThreadLocal();

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类是通过一个静态的ThreadLocal变量来为每个线程存储一个独立的Looper对象。每个线程只能创建一个Looper对象,否则将抛出异常。

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

在Looper的构造方法中,创建了与线程相关联的MessageQueue。

接着我们来看启动消息循环的Looper.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;
    ...
    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        ...
        try {
            msg.target.dispatchMessage(msg);
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        ...
        msg.recycleUnchecked();
    }
}

可以看到,loop()方法也是一个死循环,它不停地从MessageQueue中取出Message进行处理。

首先,通过myLooper()静态方法获取当前线程的Looper。然后,从这个Looper中取出当前线程的MessageQueue。最后,在死循环中通过MessageQueue的next()方法不停地获取下一条Message进行处理。当msg变量为null,即MessageQueue退出时,Looper退出循环。

根据前面的分析我们知道,Message的target就是一个Handler对象,所以最终Message是在Handler对象的dispatchMessage()方法中被处理的。由于Looper.loop()静态方法是在线程之中执行的,所以Message最终是在与Handler相关联的线程之中被处理的。如果Handler发送Message是在另外一个线程中,那么通过Handler就可以做到线程间通信。

注意:当不再使用Looper时,记得要调用quit()方法来结束消息循环。

public void quit() {
    mQueue.quit(false);
}

可以看到,quit()方法其实是调用了MessageQueue的quit()方法。MessageQueue的quit()方法退出了MessageQueue,使得MessageQueue的next()方法返回了null,最终结束了消息循环。

Handler的工作原理

Handler可以用来发送和处理Message和Runnable。每个Handler都跟一个线程和线程的MessageQueue相关联。当你创建一个新的Handler时,它就自动与一个线程和线程的MessageQueue绑定在一起。然后,我们就可以通过该Handler发送Message和投递Runnable到这个MessageQueue中。当它们从MessageQueue中被取出来时这个Handler就可以处理或者执行它们。

通常,Handler有两个作用:

  • 调度Message或者Runnable在未来的某一时刻执行。
  • 进行线程间通信或者切换到不同的线程中执行。

我们先来看Handler的两个构造方法:默认构造方法和带Callback的构造方法。这两个构造方法会将Handler对象与当前线程的Looper相关联,也即与当前线程相关联。

public Handler() {
    this(null, false);
}

public Handler(Callback callback) {
    this(callback, false);
}

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.myLooper()静态方法获取当前线程的Looper。如果当前线程没有创建Looper,即没有调用过Looper.prepare()静态方法,那么将抛出异常。然后,存储了当前线程的MessageQueue。如果设置了Callback参数,那么将其设置给mCallback成员变量。

接着我们来看Handler带Looper参数的构造方法。

public Handler(Looper looper) {
    this(looper, null, false);
}

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

带Looper参数的构造方法其实就是创建了一个与指定线程相关联的Handler。

Handler发送Message可以通过sendMessage()、sendMessageDelayed()、sendMessageAtTime()和sendEmptyMessage()等方法。投递Runnable可以通过post()、postDelayed()和postAtTime()等方法。

接着我们来看发送一条Message的sendMessage()方法。

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

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

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

可以看到,sendMessage()方法调用了sendMessageDelayed()方法。sendMessageDelayed()方法又调用了sendMessageAtTime()方法。sendMessageAtTime()方法最终调用了enqueueMessage()方法。

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

在enqueueMessage()方法中,先是指定了这条Message的target为此Handler对象。最终调用了MessageQueue的enqueueMessage()方法将Message添加到了MessageQueue之中。

接着我们来看投递一个Runnable的post()方法。

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

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

可以看到,post()方法与sendMessage()方法一样,也是调用了sendMessageDelayed()方法。Runnable对象被包装成一条Message,它被设置给了Message的callback成员变量。注意Message的callback成员变量与mCallback成员变量的不同。

最后我们来看Handler处理Message的过程。根据前面的分析我们知道,最终Handler是在dispatchMessage()方法中处理Message的。

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

private static void handleCallback(Message message) {
    message.callback.run();
}

可以看到,如果Message的callback成员变量不为null,即通过Handler投递了Runnable,那么执行此Runnable。

反之,就是通过Handler发送了Message。如果Message的mCallback成员变量不为null,即在创建Handler的时候设置了Callback参数,那么先调用此Callback对象的handleMessage()方法来处理Message。如果在创建Handler的时候没有设置Callback参数,或者Callback对象的handleMessage()方法返回了false,那么将调用Handler对象子类重写的handleMessage()方法来处理Message。

总结

Android的消息机制由Handler、MessageQueue和Looper三个部分组成。三个部分各司其职,共同完成了Android系统的线程的消息循环。我们可以使用Android的消息机制来进行线程间通信。

例子

这里举了一个简单的例子来实践Android的消息机制。例子代码地址:https://github.com/chongyucaiyan/HandlerDemo

例子的主要功能是:在子线程中更新计数(模拟耗时任务),然后通知UI线程更新界面。我们先来看一下不通过Handler直接在子线程中更新UI界面会产生什么异常。代码如下:

public class MainActivity extends AppCompatActivity {
    private TextView mTvCount;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initContentView();
        // 在子线程中更新计数
        updateCount();
    }

    private void initContentView() {
        mTvCount = (TextView) findViewById(R.id.tv_main_count);
        setCount(0);
    }

    private void setCount(int count) {
        mTvCount.setText(String.format(getString(R.string.main_count_format), count));
    }

    private void updateCount() {
        new Thread() {

            @Override
            public void run() {
                int count = 0;
                while (true) {
                    try {
                        Thread.sleep(1000);
                        count++;
                        setCount(count);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        break;
                    }
                }
            }
        }.start();
    }
}

运行例子,应用崩溃。打印的崩溃信息如下图所示:

完整的异常信息是:android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. 大概意思就是只有创建UI界面的那个线程才能更新UI界面。Android系统是在主线程(UI线程)中创建UI界面的,所以不能在子线程中直接更新UI界面。

修改代码,在UI线程中创建一个Handler,子线程通过这个Handler发送Message来通知UI线程更新界面。为了防止内存泄漏,MainHandler类声明为static内部类并且弱引用外部类。代码如下:

public class MainActivity extends AppCompatActivity {
    private static final int SET_COUNT = 1;

    private TextView mTvCount;

    private MainHandler mMainHandler = new MainHandler(this);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initContentView();
        // 在子线程中更新计数
        updateCount();
    }

    private void initContentView() {
        mTvCount = (TextView) findViewById(R.id.tv_main_count);
        setCount(0);
    }

    private void setCount(int count) {
        mTvCount.setText(String.format(getString(R.string.main_count_format), count));
    }

    private void updateCount() {
        new Thread() {

            @Override
            public void run() {
                int count = 0;
                while (true) {
                    try {
                        Thread.sleep(1000);
                        count++;
                        Message message = mMainHandler.obtainMessage(SET_COUNT, count, -1);
                        mMainHandler.sendMessage(message);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        break;
                    }
                }
            }
        }.start();
    }

    private static class MainHandler extends Handler {
        private WeakReference mMainActivityRef;

        MainHandler(MainActivity mainActivity) {
            mMainActivityRef = new WeakReference<>(mainActivity);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case SET_COUNT:
                    MainActivity mainActivity = mMainActivityRef.get();
                    if (mainActivity != null) {
                        mainActivity.setCount(msg.arg1);
                    }
                    break;

                default:
                    break;
            }
        }
    }
}

运行例子,可以看到应用正常更新UI界面。如下图所示:

Android的消息机制源码分析_第1张图片

参考

  • Android 7.1.1 (API level 25)
  • https://developer.android.com/reference/java/lang/ThreadLocal.html
  • https://developer.android.com/reference/android/os/MessageQueue.html
  • https://developer.android.com/reference/android/os/Looper.html
  • https://developer.android.com/reference/android/os/Handler.html

你可能感兴趣的:(Android源码,android)