前言
上一篇blog(处女男学Android(三)---Handler简介以及初步应用)简单的记录了关于Handler实现线程间通信的基本用法,通过一个例子实现了在主线程中给其他线程发送消息,并通过Handler的回调方法得到子线程的反馈数据从而实现了这种异步通信。本篇将通一个小例子,并结合源代码,循序渐进的记录了Handler以及相关组件(Looper、MessageQueue)的工作机制和原理。
一、初识Looper、MessageQueue
Looper和MessageQueue是同Handler一起工作的组件,通过上篇的介绍我们知道Handler可以发送消息,接收并处理消息,那么发送和接收的中间环节是如何实现的?这里就用到了Looper和MessageQueue。它们大致的工作过程是这样的:Handler发送消息到MessageQueue(消息队列),而Looper负责读取MessageQueue中的Message对象,读到的消息再交给发送消息的Handler对象进行处理,这样的解释我们似乎可以接受,但是如何考证?先不着急,首先我们通过一个例子初识一下Looper类,然后再结合源码对上面的陈述做出确切的解释和证明。
这个例子很简单,界面上只有一个Button,通过这个Button在主线程中给一个新线程中发送消息,注意是从主线程中发送消息,上一篇的例子都是在新的线程中发送消息,这样反过来我们又要如何处理?没错我们需要使用到Looper类了,先看一下代码:
Layout代码(fourth.xml):
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <Button android:id="@+id/btn_sendMessage" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Send Message" android:gravity="center" /> </LinearLayout>
Activity代码:
package com.example.handlertest; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; public class FourthActivity extends Activity{ private Button button1; private Handler handler; @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); setContentView(R.layout.fourth); button1=(Button)findViewById(R.id.btn_sendMessage); button1.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Message msg=handler.obtainMessage(); // 得到Message对象 handler.sendMessage(msg); // 发送消息 } }); Thread t=new WorkerThread(); t.start(); } class WorkerThread extends Thread{ @Override public void run(){ //准备Looper对象 Looper.prepare(); handler=new Handler(){ @Override public void handleMessage(Message msg){ System.out.println("收到了消息对象"); // 接收消息 } }; //Looper对象将不断的从消息队列中取出消息对象,然后调用handler的handlerMessage()方法,处理该消息对象 //如果消息队列当中没有对象,则该线程阻塞 Looper.loop(); } } }
我们看一下运行效果:
点击Button,确实在新的线程中接收到了消息,那么也许我们会有很多疑问,比如:
1.Looper对象是怎么工作的?它和Handler有什么联系?
2.Looper.prepare()方法和Looper.loop()方法分别有什么用?
3.MessageQueue在哪里?它又和Looper、Handler是怎么关联的?
关于这些问题,我们只能通过读Android源代码来看看它们分别是如何实现的。
二、源码分析
根据上面的代码我们发现使用了Looper.prepare()和Looper.loop(),那么我们首先来看看Looper类以及这两个方法都做了些什么:
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; 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类有MessageQueue和ThreadLocal这两个成员变量,不知道ThreadLocal对象的同学可以看看Java基础,简单的说可以理解成一个特殊的Map对象,只不过是与当前线程相关的,也就是说只能在当前线程存储对象。下面我们再看prepare方法,不难理解,它首先判断ThreadLocal成员变量是否为null,如果不为null的话会抛出异常,Only one looper may be created per thread,也就是说一个线程只能有唯一的一个Looper对象的实例,好了,如果当前线程没有Looper对象的话,那么通过构造方法new了一个Looper对象并绑定到当前线程,下面我们看看Looper的构造方法:
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
不难发现,其实Looper的构造方法就是创建了一个MessageQueue与之关联,这个MessageQueue当然就是负责管理消息了,到这里我们可以得出以下结论:
prepare方法做了两件事:
第一,创建唯一的一个Looper对象并绑定到当前线程。
第二,创建Looper对象的同时又创建了与Looper对应的MessageQueue对象负责管理消息。
准备工作完成了,下来我们看看Looper是如何读取MessageQueue的Message对象的,很明显,是通过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(); } }
public static Looper myLooper() { return sThreadLocal.get(); }
/*package*/ Handler target;
/** * Subclasses must implement this to receive messages. */ public void 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); } }
先不管前面的判断,很明显最后调用的是handleMessage(msg)方法,而Handler的handleMessage(msg)又是一个空方法,为什么呢?因为消息的最终回调是由我们控制的,这就是我们创建Handler的时候要重写handleMessage(msg)这个方法的原因。
说了这么多,我们已经知道了如果通过Looper循环MessageQueue取出消息,取出之后如何处理消息,还有一点没有说,就是如何发送消息?消息是怎么发送到指定的MessageQueue?换句话说,就是Handler是如何和MessageQueue相关联的,下面我们看看Handler的源代码:
public Handler() { this(null, false); }
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在初始化时调用了Looper.myLooper()得到Looper对象的实例,并通过这个实例进而得到MessageQueue对象,并将这两个对象和当前的Handler绑定起来。
最后我们看看常用的发送消息方法,看看是Handler是如何将Message放到MessageQueue中去的:
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); }
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
不难发现,sendMessage(msg)最后是跳到了enqueueMessage这个方法,第一行就是将当前的handler赋值给了Message的target属性,最终会调用queue的enqueueMessage的方法,也就是说handler发出的消息,最终会保存到消息队列中去。
至此整个过程就已经介绍完毕,下面再对这个过程做一个总结:
1.Looper.prepare()方法会在当前线程实例化唯一的一个Looper对象,实例化Looper对象的同时会实例化一个MessageQueue对象保存在Looper中。因为Looper在线程中是唯一的,所以MessageQueue自然也是唯一的,这样在一个线程中就确定了一个Looper和MessageQueue的对应关系。
2.Looper.loop()方法会进入死循环,不断的从MessageQueue中读取Message对象,并通过msg.target.dispatchMessage(msg)方法将Message对象交给Handler的dispatchMessage(msg)方法去处理。
3.实例化Handler的同时,会得到当前线程的Looper对象以及MessageQueue对象,这样三者就能关联上了。
4.通过sendMessage(msg)将当前的Handler赋值给Message的target属性,并将消息加入到MessageQueue。
5.实例化Handler时,重写handleMessage(msg),最终在dispatchMessage(msg)进行调用。
最后说一点,上一篇博客的例子为什么没有用到Looper对象呢?因为在UI线程(主线程)中,系统已经初始化了一个Looper对象,因此程序直接创建一个Handler对象即可发送消息、处理消息了。
三、总结
本篇blog主要介绍了Handler、Looper、MessageQueue这三个组件的工作原理和机制,读了源码之后也清楚它们是如何联系起来的,如何工作的,看到这里相信我们上面的三个疑问也都应该迎刃而解了,虽然源码不算很长,但我确实也看了很长时间,而且还没有完全消化(脑子笨),这种设计思想确实非常直接继续深入学习和理解,后续还会有一篇关于Handler的介绍,主要是记录Handler的post方法。先到这里吧,让我再好好理一理!!