Android消息处理机制——Looper、Handler、Message 源码分析
16lz
2021-01-25
前言
虽然一直在做应用层开发,但是我们组是核心系统BSP,了解底层了解Android的运行机制还是很有必要的。就应用程序而言,Android系统中的Java应用程序和其他系统上相同,都是靠消息驱动来工作的,它们大致的工作原理如下:- 有一个消息队列,可以往这个消息队列中投递消息。
- 有一个消息循环,不断从消息队列中取出消息,然后处理 。
Looper类分析
在分析之前,我先把Looper类的源码show出来,非常精简的代码,源码如下(frameworks/base/core/java/android/os/Looper.java):public final class Looper { private static final String TAG = "Looper"; // sThreadLocal.get() will return null unless you've called prepare(). static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); private static Looper sMainLooper; // guarded by Looper.class final MessageQueue mQueue; final Thread mThread; private Printer mLogging; /** Initialize the current thread as a looper. * This gives you a chance to create handlers that then reference * this looper, before actually starting the loop. Be sure to call * {@link #loop()} after calling this method, and end it by calling * {@link #quit()}. */ 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)); } /** * Initialize the current thread as a looper, marking it as an * application's main looper. The main looper for your application * is created by the Android environment, so you should never need * to call this function yourself. See also: {@link #prepare()} */ public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } } /** Returns the application's main looper, which lives in the main thread of the application. */ public static Looper getMainLooper() { synchronized (Looper.class) { return sMainLooper; } } /** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the 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; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. 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; } // This must be in a local variable, in case a UI event sets the logger Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } msg.target.dispatchMessage(msg); if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } msg.recycle(); } } /** * Return the Looper object associated with the current thread. Returns * null if the calling thread is not associated with a Looper. */ public static Looper myLooper() { return sThreadLocal.get(); } public void setMessageLogging(Printer printer) { mLogging = printer; } /** * Return the {@link MessageQueue} object associated with the current * thread. This must be called from a thread running a Looper, or a * NullPointerException will be thrown. */ public static MessageQueue myQueue() { return myLooper().mQueue; } private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); } /** * Returns true if the current thread is this looper's thread. * @hide */ public boolean isCurrentThread() { return Thread.currentThread() == mThread; } public void quit() { mQueue.quit(false); } public void quitSafely() { mQueue.quit(true); } public int postSyncBarrier() { return mQueue.enqueueSyncBarrier(SystemClock.uptimeMillis()); } public void removeSyncBarrier(int token) { mQueue.removeSyncBarrier(token); } /** * Return the Thread associated with this Looper. */ public Thread getThread() { return mThread; } /** @hide */ public MessageQueue getQueue() { return mQueue; } /** * Return whether this looper's thread is currently idle, waiting for new work * to do. This is intrinsically racy, since its state can change before you get * the result back. * @hide */ public boolean isIdling() { return mQueue.isIdling(); } public void dump(Printer pw, String prefix) { pw.println(prefix + toString()); mQueue.dump(pw, prefix + " "); } public String toString() { return "Looper (" + mThread.getName() + ", tid " + mThread.getId() + ") {" + Integer.toHexString(System.identityHashCode(this)) + "}"; }}Looper字面意思是“循环”,它被设计用来将一个普通的Thread线程变成Looper Thread线程。所谓Looper线程就是循环工作的线程,在程序开发(尤其是GUI开发)中,我们经常会使用到一个循环执行的线程,有新任务就立刻执行,没有新任务就循环等待。使用Looper创建Looper Thread很简单,示例代码如下:
package com.example.testlibrary;import android.os.Handler;import android.os.Looper;public class LooperTheread extends Thread{public Handler mhHandler;@Overridepublic void run() {// 1. 调用LooperLooper.prepare();// ... 其他处理,例如实例化handler// 2. 进入消息循环Looper.loop();}}通过1、2两步核心代码,你的线程就升级为Looper线程了。下面,我们对两个关键调用1、2进行逐一分析。
Looper.prepare()
在调用prepare的线程中,new了一个Looper对象,并将这个Looper对象保存在这个调用线程的ThreadLocal中。而Looper对象内部封装了一个消息队列。我们来看一下Looper类的源码。第一个调用函数是Looper的prepare函数,它的源码如下:
// 每个线程中的Looper对象其实是一个ThreadLocal,即线程本地存储(TLS)对象 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)); }根据上面的源码可知,prepare会在调用线程的局部变量中设置一个Looper对象,并且一个Thread只能有一个Looper对象。这个调用线程就是LooperThread的run线程。来看一下Looper对象的构造源码:
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }通过源码,我们可以轻松了解Looper的工作方式,其核心就是将Looper对象保存到当前线程的ThreadLocal中,并且保证该Looper对象只new一次。如果不理解ThreadLocal,可以参考我这篇文章: 正确理解ThreadLocal
Looper循环
调用了Loop方法后,Looper线程就开始真正的工作了,它不断从自己的MessageQueue中取出对头的信息(也叫任务)执行,如图所示:其实现源码如下所示(这里我做了一些修整,去掉不影响主线的代码):
/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the 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."); } // 取出这个Looper的消息队列 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; } // 处理消息,Message对象中有一个target,它是Handler类型 msg.target.dispatchMessage(msg); msg.recycle(); } } /** * Return the Looper object associated with the current thread. Returns * null if the calling thread is not associated with a Looper. */ public static Looper myLooper() { return sThreadLocal.get(); }通过上面的分析会发现,Looper的作用是:
- 封装了一个消息队列。
- Looper的prepare函数把这个Looper和调用prepare的线程(也就是最终处理的线程)绑定在一起,通过ThreadLocal机制实现的。
- 处理线程调用loop函数,处理来自该消息队列的消息。
Handler分析
什么是handler?handler扮演了往MessageQueue里添加消息和处理消息的角色(只处理由自己发出的消息),即通过MessageQueue它要执行一个任务(sendMessage),并在loop到自己的时候执行该任务(handleMessage),整个过程是异步的。初识Handler
Handler中的所包括的成员变量:final MessageQueue mQueue;// Handler中也有一个消息队列 final Looper mLooper;// 也有一个Looper final Callback mCallback;// 有一个回调类这几个成员变量的使用,需要分析Handler的构造函数。Handler有N多构造函数,但是我们只分析最简单的情况,在当前线程中直接new一个Handler(Handler handler = new Handler())。我们看一下构造函数是如何完成初始化操作的(frameworks/base/core/java/android/os/Handler.java):
public Handler() { this(null, false); } /** * Use the {@link Looper} for the current thread with the specified callback interface * and set whether the handler should be asynchronous. * * Handlers are synchronous by default unless this constructor is used to make * one that is strictly asynchronous. * * Asynchronous messages represent interrupts or events that do not require global ordering * with represent to synchronous messages. Asynchronous messages are not subject to * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}. * * @param callback The callback interface in which to handle messages, or null. * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for * each {@link Message} that is sent to it or {@link Runnable} that is posted to it. * * @hide */ public Handler(Callback callback, boolean async) { if (FIND_POTENTIAL_LEAKS) { final Class<? extends Handler> klass = getClass(); if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) { Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName()); } } 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中的mLooper是从Looper.myLooper()函数获取来的,而这个函数的定义我再复制一下,如下所示:
/** * Return the Looper object associated with the current thread. Returns * null if the calling thread is not associated with a Looper. */ public static Looper myLooper() { return sThreadLocal.get(); }google源码的注释也是很清楚的。可以看到,Handler中的Looper对象是Handler对象所属线程的Looper对象。如果Handler是在UI线程中实例化的,那Looper对象就是UI线程的对象。如果Handler是在子线程中实例化的,那Looper对象就是子线程的Looper对象(基于ThreadLocal机制实现)。
Handler真面目
由上面分析可知,Handler中的消息队列实际上就是Handler所属线程的Looper对象的消息队列,我们可以为之前的LooperThread类增加Handler,代码如下:public class LooperThread extends Thread{public Handler mhHandler;@Overridepublic void run() {// 1. 调用LooperLooper.prepare();// ... 其他处理,例如实例化handlerHandler handler = new Handler();// 2. 进入消息循环Looper.loop();}}加入Handler的效果图如下所示: 问一个问题,假设没有Handler,我们该如何往Looper的MessageQueue里插入消息呢?这里我说一个原始的思路:
- 调用Looper的myQueue,它将返回消息队列对象MessageQueue。
- 构造一个Message,填充它的成员,尤其是target对象。
- 调用MessageQueue的enqueueMessage,将消息插入到消息队列中。
- post(Runnable)
- postAtTime(Runnable, long)
- postDelayed(Runnable, long)
- sendEmptyMessage(int)
- sendMessage(Message)
- sendMessageAtTime(Message, long)
- sendMessageDelayed(Message, long)
public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); } private static Message getPostMessage(Runnable r) { Message m = Message.obtain();// 得到空的message m.callback = r;// 将runnable设置为message的callback return m; } public final boolean sendMessageDelayed(Message msg, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); }最终发送消息都会调用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); } private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this;// 将Message的target设置为当前的Handler,然后将消息自己加到消息队列中 if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
Handler处理消息
讲完了消息发送,再看一下Handler是如何处理消息的。消息的处理是通过核心方法dispatchMessage(Message msg)与钩子方法handleMessage(Message msg)完成的,源码如下:/** * Handle system messages here. */ 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(); } /** * Subclasses must implement this to receive messages. */ public void handleMessage(Message msg) { }dispatchMessage定义了一套消息处理的优先级机制,它们分别是:
- 如果Message自带了callback处理,则交给callback处理。例如上文分析的,Handler里通过post(Runnable r)发生一个Runnable对象,则msg的callback对象就被赋值为Runnable对象。
- 如果Handler设置了全局的mCallback,则交给mCallback处理。
- 如果上述都没有,该消息会被交给Handler子类实现的handlerMessage(Message msg)来处理。当然,这需要从Handler派生并重写HandlerMessage函数。
Handler的用处
看完了Handler的发送消息和处理消息,我们来学习一下Handler被称为异步处理大师的真正牛逼之处。Hanlder有两个重要的特点: 1. handler可以在任意线程上发送消息,这些消息会被添加到Handler所属线程的Looper对象的消息队列里。2. handler是在实例化它的线程中处理消息的。
这解决了Android经典的不能在非主线程中更新UI的问题。Android的主线程也是一个Looper线程,我们在其中创建的Handler将默认关联主线程Looper的消息队列。因此,我们可以在主线程创建Handler对象,在耗时的子线程获取UI信息后,通过主线程的Handler对象引用来发生消息给主线程,通知修改UI,当然消息了还可以包含具体的UI数据。
Message
在整个消息处理机制中,Message又叫做task,封装了任务携带的消息和处理该任务的handler。Message的源码比较简单,源码位置(frameworks/base/core/java/android/os/Message.java)这里简单说明几点注意事项: 1. 尽管Message有public的默认构造方法,但是你应该通过Message.obtain()来从消息池中获得空消息对象,以节省资源,源码如下:/** * Return a new Message instance from the global pool. Allows us to * avoid allocating new objects in many cases. */ public static Message obtain() { synchronized (sPoolSync) { if (sPool != null) { Message m = sPool; sPool = m.next; m.next = null; sPoolSize--; return m; } } return new Message(); } /** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}). */ public Message() { }2. 如果你的Message只需要携带简单的int信息,应该优先使用Message.arg1和Message.arg2来传递信息,这比使用Bundler节省内存。
/** * arg1 and arg2 are lower-cost alternatives to using * {@link #setData(Bundle) setData()} if you only need to store a * few integer values. */ public int arg1; /** * arg1 and arg2 are lower-cost alternatives to using * {@link #setData(Bundle) setData()} if you only need to store a * few integer values. */ public int arg2; /** * Sets a Bundle of arbitrary data values. Use arg1 and arg1 members * as a lower cost way to send a few simple integer values, if you can. * @see #getData() * @see #peekData() */ public void setData(Bundle data) { this.data = data; }3. 用Message.what来标识信息,以便用不同方式处理message。
示例代码
写了一个子线程利用主线程Handler更新UI的示例代码,如下:public class MainActivity extends Activity {TextView mTextView;MyHandler mHandler = new MyHandler();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mTextView = (TextView)findViewById(R.id.test1);new Thread(new UpdateTitleTask(mHandler)).start();}private class MyHandler extends Handler {@Overridepublic void handleMessage(Message msg) {Bundle bundle = msg.getData();mTextView.setText(bundle.getString("title", ""));}}}
public class UpdateTitleTask implements Runnable{private Handler handler;public UpdateTitleTask(Handler handler) {this.handler = handler;}private Message prepareMsg() {Message msg = Message.obtain();Bundle bundle = new Bundle();bundle.putString("title", "From Update Task");;msg.setData(bundle);return msg;}@Overridepublic void run() {try {Thread.sleep(2000);Message msg = prepareMsg();handler.sendMessage(msg);} catch (InterruptedException e) {}}}
参考文献
1. 《深入理解Android 卷一》 2. http://www.cnblogs.com/codingmyworld/archive/2011/09/12/2174255.html更多相关文章
- Android文件下载之进度检测
- android GC内存泄露问题
- Android:WebView全面总结
- 读书笔记 Pro Android(安卓)3 第2章 节选 StrictMode 严苛模式
- Android* 教程:使用英特尔® 线程构建模块编写多线程应用
- Android中AsyncTask与handler用法实例分析
- 基于android的Socket通信
- Android操作JNI函数以及复杂对象传递
- android实现简单的聊天室