您的位置:首页 > 移动开发 > Android开发

Android的Handler Looper Message机制应用实例与详解(二) 推荐

2014-10-15 12:50 696 查看
上一篇博文给出了Android中基于Handler Looper机制实现线程间通信的两个典型实例。本文将对该机制的基本原理进行较深入的研究。个人认为,学好Android编程最好的老师就是Android的源代码,下面将基于Android-19的源码进行分析,重点阐述分析思路。

要分析Handler Looper机制,自然想到去看Handler类和Looper类的源码(分别位于Handler.java和Looper.java两个文件中)。简单阅读两个类的描述后,在Looper类的描述中能找到以下一段示例代码。
* <p>This is a typical example of the implementation of a Looper thread,
* using the separation of {@link #prepare} and {@link #loop} to create an
* initial Handler to communicate with the Looper.
*
* <pre>
*  class LooperThread extends Thread {
*      public Handler mHandler;
*
*      public void run() {
*          Looper.prepare();
*
*          mHandler = new Handler() {
*              public void handleMessage(Message msg) {
*                  // process incoming messages here
*              }
*          };
*
*          Looper.loop();
*      }
*  }</pre>
*/
这段代码给出了Handler Looper机制实现进程间通信的三大基本步骤,包括Looper的两个函数prepare()和loop(),以及Handler的handleMessage函数。上一篇博文中实例二模拟子线程向UI主线程传递信息的程序就基本上是直接copy这段示例代码实现的。

先看第一个步骤:调用Looper.prepare()函数,猜测应该是创建Looper对象,做些初始化工作。代码如下:
/** 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);
}
直接调用了重载函数prepare(true);

//ThreadLocal实例为多个线程共享,但保证每个线程的存储空间相互独立
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
//消息队列
final MessageQueue mQueue;
//当前线程引用
final Thread mThread;
private static void prepare(boolean quitAllowed) {
//保证一个线程最多只能创建一个Looper对象。
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//创建Looper对象并存储到当前线程独立的存储空间中
sThreadLocal.set(new Looper(quitAllowed));
}

private Looper(boolean quitAllowed) {
//创建消息队列(线程间即通过该消息队列实现通信)
mQueue = new MessageQueue(quitAllowed);
//获得当前线程的引用
mThread = Thread.currentThread();
}
看到这里明白了,Looper的prepare函数实际上创建了Looper对象并把对象保存到当前线程独立的存储空间中。这里,Looper的构造函数是私有的,所以外部无法直接通过new Looper()随意创建Looper对象。而只能通过Looper的prepare()函数创建。这样做能够保证对于某一个线程,最多只会创建一个Looper对象的实例,这实际上就是设计模拟中的单体模式。此外,在Looper的私有构造函数中还创建了消息队列并获得当前线程(即创建Looper的线程)的引用。
先跳过Handler.handleMessage(Message msg),直接看Looper.loop()的实现。
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {ui
//myLooper()函数通过sThreadLocal.get()判断当前线程是否已经创建了Looper的实例
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);
}
//这里是关键,调用了dispatchMessage函数对从消息队列取出的msg进行分派
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();
}
}
到这里,Looper扮演的角色已经明朗了,主要就是通过loop()函数中的那个无限循环不断从消息队列中取出消息,并通过dispatchMessage()方法将消息派送出去。那么消息队列中的消息从哪里来,又会被派送到哪里呢?
先来分析第一个问题,消息从哪里来。上一篇博文的实例中,消息源线程均通过调用Handler的sendMessage()函数来发送消息。进入Handler.java文件看其中的sendMessage()函数。
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
这里调用了sendMessageDelayed,延时0毫秒。
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);
}
再看enqueueMessage函数。

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//设置target handler
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//将消息插入到消息队列中
return queue.enqueueMessage(msg, uptimeMillis);
}
看到这里已经明朗了,消息源线程通过Handler.sendMessage发送消息,实际上就是把消息插入了与之关联的消息队列中。在enqueueMessage函数中有一条关键语句msg.target = this,通过这条语句就把Handler和Looper关联起来了(在Looper.loop()的循环中就是通过msg.target属性找到发送消息的Handler并调用其dispatchMessage()函数派发消息的).
搞清楚了消息从哪里来,接下来分析消息被派发到哪里,接着看dispatchMessage()函数的实现。
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
//若消息对象实现了其中的Runnable接口,调用对应的回调函数,即为message.callback.run())
if (msg.callback != null) {
handleCallback(msg);
} else {
//若实现了Handler类的Callback接口,调用接口的回调函数
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//将消息返回Handler的消息处理回调函数(是Handler的成员函数,示例代码中回调的就是该函数)
handleMessage(msg);
}
}
可见,dispatchMessage函数中根据msg,handler对象的设置情况调用相应的消息处理回调函数,我们只需要在这个回调函数中添加代码,就可以进行消息处理。示例代码的第二个步骤中的handleMessage函数就是在这里被回调的。
下面回到示例代码中的第二个步骤:
*    mHandler = new Handler() {
*         public void handleMessage(Message msg) {
*         // process incoming messages here
*         }
*    };
这段代码创建了Handler的对象,并覆盖了其中的HandleMessage方法,用户可以添加自己的消息处理函数。 handleMessage回调函数上面已经分析过了,下面主要看看创建handler对象时都做了哪些事情。转入Handler.java文件,看Handler的构造函数(找不带参数那个)。

/**
* Default constructor associates this handler with the {@link Looper} for the
* current thread.
*
* If this thread does not have a looper, this handler won't be able to receive messages
* so an exception is thrown.
*/
public Handler() {
this(null, false);
}
调用了下面的双参数的构造函数。
final Looper mLooper;
final MessageQueue mQueue;
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());
}
}
//获得当前线程Looper对象的引用
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
//获得Looper关联的消息队列
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
到这里发现Handler的构造函数主要做了两件事:1)获得当前线程Looper对象的应用.2)获得与Looper关联的消息队列的引用。到这里,Handler、Looper、消息队列三者就已经关联起来了。

下面通过一个示意图对上面的分析进行总结。




由图可见,基于Handler Looper机制传递消息主要包括以下几个步骤。
(1)目标线程调用Looper.prepare()创建Looper对象和消息队列。
(2)目标线程通过new Handler()创建handler对象,将Handler,Looper,消息队列三者关联起来。并覆盖其handleMessage函数。
(3)目标线程调用Looper.loop()监听消息队列。
(4)消息源线程调用Handler.sendMessage发送消息。
(5)消息源线程调用MessageQueue.enqueueMessage将待发消息插入消息队列。
(6)目标线程的loop()检测到消息队列有消息插入,将其取出。
(7)目标线程将取出的消息通过Handler.dispatchMessage派发给Handler.handleMessage进行消息处理。
到这里整个Android的Handler Looper机制传递消息原理就分析完毕了。还有一个问题值得一提,回顾一下上一篇博文的示例1模拟从网络上下载数据的程序,UI主线程只通过new Handler()创建了Handler对象的实例并覆盖了其handleMessage函数。在代码中并没有看到调用Looper.prepare和Looper.loop(),那么UI主线程中没有创建Looper对象吗?下面就来分析这个问题,既然是UI主线程,那么自然是在启动应用时候由系统自动创建的,创建过程中是否已经创建了Looper对象并调用loop()进行监听了呢?转到ActivityThread.java,找到其中的main函数,这里即为Android程序的入口。在其中能看到以下两行代码。
Looper.prepareMainLooper();
...... ......
Looper.loop();
再看prepareMainLooper函数的实现:

public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
在这里调用了prepare创建Looper对象。所以说,对于UI主线程而言,其Looper对象是由系统创建好的,用户就无需自行创建了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐