Android的消息机制源码分析

来源:互联网 发布:启动sql server服务 编辑:程序博客网 时间:2024/05/21 16:59

前言

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<Looper> sThreadLocal = new ThreadLocal<Looper>();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();    }}

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

子线程中更新UI界面的崩溃信息.png

完整的异常信息是: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<MainActivity> 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界面。如下图所示:

界面正常更新.png

参考

  • 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
原创粉丝点击