Android异步消息机制 Looper
如果不了解handler,请先看一下我的上一篇handler详解
android handler机制源码解析
先简单了解下looper的作用
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
从looper的构造函数可以看到:looper必须和messageQueue以及thread相关,之前也提到过每个looper有一个自己的messageQueue,每个thread最多只有一个looper。looper创建好之后,调用loop()开始循环,只要对应的msgQueue里面有msg了(handler发送过来的),就取出来处理,调用对应的handler去dispatchMessage
Looper.prepare()
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源码中可以看到,会去new一个当前线程的looper放在ThreadLocal里面。ThreadLocal可以简单理解为一个map,可以通过thread找到looper;由于是threadLocal的put方法,prepare的结果只可能是增加一个或者更新一个,所以一个线程最多只持有一个looper;
- 还记得之前说的,handler创建也需要对应的looper吗?因为handler创建的时候,如果默认什么参数都没有,会根据Thread.currentThread去threadLocal里面把对应的looper取出来。如果这时候,该线程事先没有looper,那么就会报错。
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;
}
看源码的log: "Can't create handler inside thread that has not called Looper.prepare()",是不是这个异常很熟悉?
所以如果直接new handler()必须要先创建looper(Looper.prepare);
那么可能有人会提出疑问,平时我直接new一个handler(),然后运行也没见崩溃啊。这是因为啊,在ui线程创建的时候,ActivityThread main函数入口就已经自己创建了looper,而且这个smainLooper是ui线程专有的(可能因为UI线程是唯一的,所以smainLooper也是唯一的吧)。自己new的一个线程构造handler之前一定记得Looper.prepare,然后调用Looper.loop()开始循环处理发送到messageQueue的消息。
关于ActivityThread
位于frameWork层,直接查找不能找到该类。除了导入源码,也可以通过其他方式看到该类。
比如在activity类click事件里面触发断点,然后利用debug的frames查看帧调用关系,往上面找就可以找到activityThread。
activityThread作为主线程的启动,应该是会加载一些类的。从ClassLoader的loadClass方法去查找它的调用,可以看到activityThread确实调用了,然后从这里也可以进入activityThread。(安装jd-intelij插件反编译源码)
- 当然我们也可以给handler传一个looper,而不是让它默认去当前线程找looper。常用的就是把主线程的looper传进去吧。
new Handler(Looper.getMainLooper()).post(Runnable);
没错,这又是一种切到ui线程的方式,和runOnUiThread性质是一样的,走handler.post,最后都是把msg入队到UI线程的msgQueue(还记得起为什么runOnUiThread会切到ui线程吗,不懂的请看第一篇);
Looper.loop()
这个方法其实就是开始循环,一直监听;从执行这个函数的线程里面把对应的looper取出来,拿到对应的msgQueue。把里面的消息取出来,调用对应的handler去分发处理消息,后面的就是handler处理消息了。
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
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
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.recycleUnchecked();
}
}
所以在子线程里面要记得调用loop();不然就会出现msg发送倒是成功了,但是不会有人取出来对这个msg进行分发处理。
贴一个简单的demo吧:
//ui线程
private TestThread testThread = new TestThread();
Log.v("tag","ui主线程发送msg "+"当前线程:"+Thread.currentThread().getId());
testThread.mhandler.sendEmptyMessage(0);
//另起一个线程
class TestThread extends Thread{
private Handler mhandler;
@Override
public void run() {
Looper.prepare();
mhandler = new Handler() {
@Override
public void handleMessage(Message msg) {
Log.v("tag", "子线程接收msg "+"当前线程: "+Thread.currentThread().getId());
}
};
Looper.loop();
}
}
运行结果:
在子线程创建looper,android提供了handlerThread,里面的run方法也是在新线程里面looper.prepare looper.run。在子线程运行结束的时候,可以调用quite方法退出loop()循环。
可以看到UI线程发的消息已经传到子线程处理了(主线程的threadId为1,唯一的主线程所以才配了个唯一的sMainLooper吧)
还有个问题:为什么looper知道这个msg该交给哪个handler处理呢?
其实很简单:handler发送消息入队的时候,就把自己的引用给msg了,msg有一个target属性(就是上面的msg.target.dispatchMessage(msg);)就是专门用来保存与handler的连接的。looper取出message,再找到msg对应的handler分发。
如果有讲的不清楚或者有误的地方,请指出。大家共同进步,谢谢
RNG凉了,贼难受 = =
网友评论