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(); }}
运行例子,应用崩溃。打印的崩溃信息如下图所示:
完整的异常信息是: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界面。如下图所示:
参考
- 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的消息机制
- Android 消息机制的源码分析
- Android的消息机制源码分析
- android消息机制源码分析
- Android 消息机制源码分析
- Android消息机制源码分析
- Android消息机制源码分析
- Android 消息机制源码分析
- Android消息机制源码分析
- Android消息机制源码分析
- Android消息机制源码分析
- android的消息处理机制(图文+源码分析)
- Android基于源码分析Handler的消息机制
- Android 消息机制 - Handler, Looper, Message, MessageQueue 的源码分析
- 从源码的角度分析Android消息处理机制
- 通过源码分析Android 的消息处理机制
- Android异步消息处理机制的源码分析
- [PAT乙级]1019. 数字黑洞 (20)
- pat 1005. 继续(3n+1)猜想 (25)
- 蓝桥杯练习题 BEGIN-3 圆的面积
- 445. Add Two Numbers II(Java)
- Angular2 Http
- Android的消息机制源码分析
- 蓝桥杯练习题 BEGIN-4 Fibonacci数列
- pat 1006. 换个格式输出整数 (15)
- 新巴巴运动网上商城 项目 快速搭建 教程 The new babar sports online mall project quickly builds a tutorial
- mysql5.7.19 winx64解压缩版安装配置教程
- 详解CUDA核函数及运行时参数
- 方正科技索赔案的法理根据
- C11哈希
- 3m安装