问个问题:如果两个handler共享一个Looper,那么HandlerA发送的消息,HandlerB会不会收到嗫?
如果你不知道,那就看看我这篇文章,如果知道就别看了。。。
Handler大家一定太熟悉了,就是发送Message的,然后在handler关联的线程里处理Message。一般用来通知消息或者切换线程工作之类的。
一个Thread对应一个Looper,Looper就是一个循环体,Looper的作用就是给Thread用来循环处理消息的。当然如果你的Thread创建不需要sendMessage什么的,那当然就不需要创建一个Looper,比如你只是打印个字符串而已。。。
Looper既然是循环处理消息的话,那Looper中肯定有个Message的队列,没错,就是MessageQueue。MessageQueue就是记录Message队列的,采用链表的方式,并且记录列表头部指针(Java没有指针的概念,就是用个变量记录第一个Message。。。)。
看下Looper使用的例子:
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();
}
}
这是个很简单的例子,在创建Thread的时候,先准备好Looper,只需要调用Looper.prepare()就可以了,看下prepare方法:
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));
}
public void set(T value) {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values == null) {
values = initializeValues(currentThread);
}
values.put(this, value);
}
大概就是得到当前的Thread,然后new一个Looper关联到Thread上。接下来就是生命一个Handler,看下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,通过Looper.myLooper得到,然后记录Looper和Looper的MessageQueue,当然你可以调用Handler其他的构造函数来指定Looper等。
然后调用Looper.loop。loop函数中有一个死循环,是不断的读取MessageQueue中的Message来处理,如果队列为空,则该死循环会阻塞,知道有新的Message到来才会唤起。
讲了这么多,该讲讲主人公Message了,Message主要有以下的参数:
public final class Message implements Parcelable {
//Message传递的一些参数
public int what;
public int arg1;
public int arg2;
public Object obj;
//记录发送方
public Messenger replyTo;
public int sendingUid = -1;
//记录当前Message是不是正在被使用,因为Message可以重复利用,所以需要有变量记录
/*package*/ static final int FLAG_IN_USE = 1 << 0;
/** If set message is asynchronous */
/*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1;
/** Flags to clear in the copyFrom method */
/*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;
/*package*/ int flags;
//在MessageQueue中排序时是通过该值来排序
long when;
/*package*/ Bundle data;
//记录该Message是要发送给哪个Handler来处理
Handler target;
/*package*/ Runnable callback;
//刚刚提到在MessageQueue中是以链表的方式记录,所以next相当于指针。
/*package*/ Message next;
}
从参数中,可以将Message分为两类,一种是携带参数信息的(what, arg1, arg2等),一种是携带runnable的。后者用handler执行的时候,就是直接调用runnable.run方法。对应到handler的话就是两种发消息的方法:
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
如果调用post的话,会将runnable封装到Message中。
那如何创建一个Message呢?
可以通过调用Message.obtain(…)或者handler.obtainMessage(…)来调用,都是一样的,handler.obtainMessage最后也都是调用Message里面的方法。
在创建Message的时候,要指定Message的target是谁!!!当从MessageQueue中取出来会将该message发给message.target来处理。所以如果两个handler共享一个Loop,通过HandlerA发送的消息只能由HandlerA来处理,HandlerB不会收到。
下面问个问题:
如何在一个子线程中弹toast?
是这样么?
new Thread(new Runnable() {
@Override
public void run() {
Toast.makeText(TestActivity.this,"lall",Toast.LENGTH_LONG).show();
}
}).start();
no,no,no。当你这么调的时候会发现出错了。这里要提醒下Toast中用到了handler。所以如果你直接像上面这么写会出错,因为在该子线程中Looper还没prepare好,所以创建handler的时候会出错。所以这就要引入一个新的Thread,就是handlerThread,该thread在创建的时候就会自己创建一个Looper并绑定,所以你可以这么做:
HandlerThread t = new HandlerThread("thread");
t.start();
Handler handler = new Handler(t.getLooper());
handler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(TestActivity.this,"ddd",Toast.LENGTH_LONG).show();
}
});