在前一节中讨论了Looper类中的两个主要方法,也分析了Looper、Message和Handler的关系。那么下面将分析Handler这个类。
一、先看看Handler类中的几个变量和几个构造函数
final MessageQueue mQueue;
final Looper mLooper;
final Callback mCallback;
上面三个变量,在Handler中,也有一个消息队列mQueue;一个Looper,还有一个回调的类Callback.。这三个变量是怎么初始化的呢,那么就要看Handler的构造函数了。Handler总共有4个构造函数,以下是代码:
/**
* Default constructor associates this handler with the queue for the
* current thread.
*
* If there isn't one, this handler won't be able to receive messages.
*/
public Handler() {
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();//获取调用线程的Looper
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;//得到Looper的消息队列。
mCallback = null;
}
/**
* Constructor associates this handler with the queue for the
* current thread and takes a callback interface in which you can handle
* messages.
*/
public Handler(Callback callback) {
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;
}
/**
* Use the provided queue instead of the default one.
*/
public Handler(Looper looper) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = null;
}
/**
* Use the provided queue instead of the default one and take a callback
* interface in which to handle messages.
*/
public Handler(Looper looper, Callback callback) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
}
上面四个构造函数已经说明了,Looper就是当前调用线程的looper,mQueue也是Looper中的消息队列。那么Handler这样做的真正原因呢?
二、Handler的原理
假设一个问题,当我们要往Looper的消息队列中插入消息时,我们应该怎么做呢?如果没有Handler,我们可能会这样做:
1、获取Looper中的消息队列mQueue,也就是Looper中的myQueue()方法,得到的是MessageQueue对象;
2、构造一个Message对象,将相应的变量复制,特别是将里面的target变量设为处理消息的Handler;
3、将上面的message对象,插入到上面的mQueue队列中。
基本的原理也就如此,但是这样却有一些麻烦,也会甙类一些不确定的错误。而引入Handler,就让这些工作变得简单了。通过Handler中的其他方法就能看到,Handler提供了一些列方法,帮助我们完成创建消息,插入消息的工作,下面是部分方法:
public final boolean hasMessages(int what) //查看当前消息队列中是否有消息码为what的消息。
public final Message obtainMessage(int what)//从Handler中创建一个消息码为what的消息
public final void removeMessages(int what)//从消息队列中,移除消息码为what的消息
public final boolean sendEmptyMessage(int what) //发送一个只有消息码what的空消息
public final boolean sendMessage(Message msg)//发送一个消息,该消息将被添加到队尾
public final boolean sendMessageAtFrontOfQueue(Message msg)//发送一个消息,将消息添加到队头
上面只列了Handler中的部分方法,稍后会在附件中附上Handler的源码。
对于几个方法,只要看了源码,就能明白其他函数的意思了。
/**
* Pushes a message onto the end of the message queue after all pending messages
* before the current time. It will be received in {@link #handleMessage},
* in the thread attached to this handler.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
/**
* Enqueue a message into the message queue after all pending messages
* before (current time + delayMillis). You will receive it in
* {@link #handleMessage}, in the thread attached to this handler.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the message will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
/**
* Enqueue a message into the message queue after all pending messages
* before the absolute time (in milliseconds) <var>uptimeMillis</var>.
* <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
* You will receive it in {@link #handleMessage}, in the thread attached
* to this handler.
*
* @param uptimeMillis The absolute time at which the message should be
* delivered, using the
* {@link android.os.SystemClock#uptimeMillis} time-base.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the message will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
boolean sent = false;
MessageQueue queue = mQueue;
if (queue != null) {
msg.target = this;//将消息的target设为自己,然后将消息加入到消息队列中。
sent = queue.enqueueMessage(msg, uptimeMillis);
}
else {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
}
return sent;
}
通过上面的部分方法,就能明白Handler的工作实际是辅助的,将对MessageQueue的操作封装起来。这里处理对内,将消息封装、添加队列,还提供了对外处理消息的接口。这就是我们经常在处理消息时,需要重写Handler的handleMessage方法的地方。
三、Handler的消息处理
在之前,我们看过Looper的方法,发现Looper在处理消息的时候,会调用target的dispatchMessage方法,就等于是把消息派发给Handler处理。那么Handler中的dispatchMessage又是怎样执行呢?
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {//如果Message有callback,则直接交给callback处理。
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {//如果handler中设置了callback,则直接交给handler的callback处理。
return;
}
}
handleMessage(msg);//最后交给子类处理。
}
}
在dispatchMessage中,定义了一套处理消息的优先机制:
1、Message如果自带了callback,则直接交由callback处理;
2、Handler如果设置了全局的mCallback,则交给mCallback处理;
3、当上述都没有处理的时候,该消息则直接交给Handler的子类来处理。这就是为什么在我们自己的代码中,通常要派生出一个Handler,然后重写HandleMessage方法。这是平常用得最多的方式。
四、Looper和Handler的同步关系
Looper和Handler是有同步关系的,这与多线程有关。举个例子:
//定义一个LooperThread类
class LooperThread extends Thread {
public Looper myLooper = null;
public void run() {//假设run()在线程2中执行
Looper.prepare();
//myLooper必须在这个线程中赋值。
myLooper = Looper.myLooper();
Looper.loop();
}
}
//下面的代码在线程1中执行,并且会创建线程2;
{
LooperThread lpThread = new LooperThread();
lpThread.start();//创建线程2
Looper looper = lpThread.myLooper;//获取线程2中的looper ***********
Handler thread2Handler = new Handler(looper);//利用线程2中的looper创建线程1中的Handler,将Handler与线程2中的looper关联。
thread2Handler.sendMessage(...);//发送消息,将交给线程2来处理。
}
上面的代码比较简单:
1、线程1中创建线程2,并且线程2通过Looper处理消息。
2、线程1中得到线程2中的looper,并且根据这个looper创建一个Handler,这样发送给该Handler的消息酱油线程2处理。
理解比较简单,但是这个地方时有问题的。在多线程中,myLooper的创建是在线程2中,而looper的赋值却是在线程1中,有可能出现,线程2的run方法还没有执行,也就是说还没有光剑myLooper,那么这时候给线程1中的looper赋值,就会变成null。
另外,
Handler thread2Handler = new Handler(looper); 不能替换成
Handler thread2Handler = new Handler(Looper.myLooper());
这是由于,myLooper()返回的是调用线程的looper,也就是线程1的Looper,而不是线程2的looper;
上面的问题就需要采用同步的方式来处理。在Android中,提供了一个HandlerThread来解决这个问题。
五、HandlerThread
HandlerThread完美的解决了上面的问题,处理了myLooper可能为空的问题。以下是HandlerThread的代码:
/**
* Handy class for starting a new thread that has a looper. The looper can then be
* used to create handler classes. Note that start() must still be called.
*/
public class HandlerThread extends Thread {
private int mPriority;
private int mTid = -1;
private Looper mLooper;
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
/**
* Constructs a HandlerThread.
* @param name
* @param priority The priority to run the thread at. The value supplied must be from
* {@link android.os.Process} and not from java.lang.Thread.
*/
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
/**
* Call back method that can be explicitly over ridden if needed to execute some
* setup before Looper loops.
*/
protected void onLooperPrepared() {
}
//线程2运行它的run函数,looper就是在run线程中创建
public void run() {
mTid = Process.myTid();
Looper.prepare();//创建这个线程上的looper
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();//通知取looper的线程1,此时looper已经创建好了。
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
/**
* This method returns the Looper associated with this thread. If this thread not been started
* or for any reason is isAlive() returns false, this method will return null. If this thread
* has been started, this method will block until the looper has been initialized.
* @return The looper.
*/
public Looper getLooper() {//线程1调用getLooper来获得新线程的Looper
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();//如果新线程还未创建Looper,则等待
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
/**
* Ask the currently running looper to quit. If the thread has not
* been started or has finished (that is if {@link #getLooper} returns
* null), then false is returned. Otherwise the looper is asked to
* quit and true is returned.
*/
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
/**
* Returns the identifier of this thread. See Process.myTid().
*/
public int getThreadId() {
return mTid;
}
}
HandlerThread很简单,就是使用了同步中的wait和notifyAll就解决了。所以在使用的时候,我们可以多使用HandlerThread,而避免出现同步的问题。
总结一下:Handler会关联一个单独的线程和消息队列。Handler默认关联主线程,虽然要提供Runnable参数 ,但默认是直接调用Runnable中的run()方法。也就是默认下会在主线程执行,如果在这里面的操作会有阻塞,界面也会卡住。
如果要在其他线程执行,可以使用HandlerThread。HandlerThread的简单用法:
HandlerThread uIhandlerThread = new HandlerThread("update");
uIhandlerThread.start();
Handler uIhandler = new Handler(uIhandlerThread.getLooper(),new Callback() {
public boolean handleMessage(Message msg) {
Bundle b = msg.getData();
int age = b.getInt("age");
String name = b.getString("name");
System.out.println("age is " + age + ", name is" + name);
System.out.println("Handler--->" + Thread.currentThread().getId());
System.out.println("handlerMessage");
return true;
}
});
这样,消息都将发到HandlerThread所在的线程中去处理。HandlerThread一般与Handler配合完成。
最后,将Looper,Handler的关系理清了,里面也有一些其他类,包括HandlerThread和Message,具体的使用以及注意,需要再实际的使用中才能明白,但是,先理清几个类之间的关系,可以避免不必要的问题。
附上几个类的下载地址: