我尽量不打错别字,用词准确,不造成阅读障碍。
今天看到一篇文章,讲解handler的消息传递机制,感觉讲的很好,于是结合自己的理解,写一篇文章。
正常使用Handler的时候是在Activity和Fragment中new Handler();并重写handleMessage(Message msg);方法,然后在哪一个地方使用handler.sendEmptyMessage(msg);方法,发送Message,方法很多,简单举例。
我的理解:Handler的机制就像生活中的你乘坐大超市那种扶梯一样,有种超市的扶梯是有人的时候才会启动。正常扶梯是停止的,因为它检测到没有人,这时候你往扶梯上一站就会触发扶梯滚动,扶梯就是MessageQueue,人就是Message,检测扶梯上是否有人就是Looper的职责,控制你走上扶梯的大脑就是Handler。这个理解其实不全面,但是大致意思是清楚了。
其实在Activity创建运行的时候是有一个入口函数的,main(String[] args)函数,就是普通java的入口函数,如下:
public static void main(String[] args) {
......
Looper.prepareMainLooper(); //创建Looper
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
Looper.loop(); //开启循环,从MessageQueue中取消息
throw new RuntimeException("Main thread loop unexpectedly exited");
}
主要是在其中调用了Looper.prepareMainLooper();
方法,这个方法是创建Looper的,相当于你创建了检测扶梯上是否有人的系统,在这个创建过程中Looper会将自己放入到一个ThreadLocal中,这是个类似于HashMap的类,可以存放数据,为什么要放进去?为了以后谁想获取Looper 的时候是直接从ThreadLocal里面获取就好了,类似于一个Looper的管理。
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
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));
}
多说一句:ThreadLocal是有其特殊性的,要不然为什么用它,存放数据的类辣么多,因为它是要在指定的线程中创建并放入数据,同时只能在那个指定的线程中取出数据,局限性很明显,但就是要这个局限性。举例:
ThreadLocal<Integer> mThreadLocal = new ThreadLocal<>();
private void testMethod() {
mThreadLocal.set(0);
Log.d(TAG, "main mThreadLocal=" + mThreadLocal.get());
new Thread("Thread1") {
@Override
public void run() {
mThreadLocal.set(1);
Log.d(TAG, "Thread1 mThreadLocal=" + mThreadLocal.get());
}
}.start();
new Thread("Thread2") {
@Override
public void run() {
mThreadLocal.set(2);
Log.d(TAG, "Thread2 mThreadLocal=" + mThreadLocal.get());
}
}.start();
Log.d(TAG, "main mThreadLocal=" + mThreadLocal.get());
}
输出结果:
main mThreadLocal=0
Thread1 mThreadLocal=1
Thread2 mThreadLocal=2
main mThreadLocal=0
这个效果其实也可以用Map实现,HashMap<Thread, Object>
或ConcurrentMap<Thread, Object>
, 之所以不用,有一段话:
“使用Map会有一些麻烦的事要处理,比如当一个线程结束的时候我们如何删除这个线程的对象副本呢?如果使用ThreadLocal就不用有这个担心了,ThreadLocal保证每个线程都保持对其线程局部变量副本的隐式引用,只要线程是活动的并且 ThreadLocal 实例是可访问的;在线程消失之后,其线程局部实例的所有副本都会被垃圾回收(除非存在对这些副本的其他引用)。更多ThreadLocal的讲解参考:Android线程管理之ThreadLocal理解及应用场景,地址:https://www.cnblogs.com/whoislcj/p/5811989.html。”
获取looper比较简单,这个myLooper()方法后面会用到多次:
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
创建了检测扶梯是否有人的系统后,这个时候MessageQueue已经存在了,因为Looper中含有MessageQueue,程序和生活还是有点不一样的。有了系统后,接下来就打开它,Looper.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;
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
msg.recycleUnchecked();
}
}
里面的死循环就是检测是否有message(人)的地方,没有message(人),就不往下走,也就不会触发发送代码,电梯就不会启动。一旦有了message,就会触发msg.target.dispatchMessage(msg);
方法,这个方法就会将message发送到你Handler中的handleMessage(Message msg)中,你就可以处理了。其中msg.tartget就是你创建的handler对象,这是在发送message的时候赋值的。
这个时候电梯已经准备好了,系统也工作了,而你其实一行代码都没有写,这都是Android为你准备的。现在你写了new Handler();构造方法其实做了很多事情:
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;
}
首先要获取mLooper,就是从ThreadLocal里面取出Looper,然后从mLooper中拿出mQueue,这样就相当于,你找到了电梯的位置,但你上不上电梯就是看你自己了。
接下来你写了一句 handler.sendEmptyMessage(msg);
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);
}
代码很清楚,会走到sendMessageAtTime(...);
方法中,这样就将queue拿出来,然后走到enqueueMessage(...);
中,这个时候msg.target = this;将handler赋值给msg.target,这样就可以使用msg.target.dispathMessage(msg);
去发送消息,这样条件都创建好了,就等你往电梯上站了,最后将message放入queue中,你走上了电梯!looper(系统)检测到了有message(人)放入了MessageQueue(电梯),然后msg.target也是有值的,就可以顺利走完msg.target.dispathMessage(msg);
方法,电梯启动,将message(人)发送到handleMessage(msg);方法中交给开发者处理;
理解可能有误,举例只是举个差不多的例子,欢迎指正;