故事要先从一个我们平常最常见的也是最简单的Handler的用法说起:
Handler myHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
tv.setText("Hello World");
break;
}
super.handleMessage(msg);
}
};
new Thread(new Runnable() {
@Override
public void run() {
//do something
Message message = Message.obtain();
message.what = 1;
myHandler.sendMessage(message);
}
}).start();
这就是我们开始学习安卓时候最常见的一段代码,子线程执行完一系列操作之后,返回主线程刷新UI界面。
一、看图说话
一切的一切都要从一张图说起:
[图片上传失败...(image-c47960-1552406344345)]
说到这里就要请出本次的几位主演了:
Handler
:它有两个作用:
- 将消息放入消息队列
- 将消息从队列中取出并执行
MessageQueue
:消息队列
Message
:消息
Looper
:为线程运行消息循环。
二、从主线程的Looper说起
知道了四个类的大概作用,我们切回到开篇的代码。
已知在activity中:
public static void main(String[] args) {
//与本文无关的代码
...
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);////建立Binder通道 (创建新线程)
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
//如果能执行下面方法,说明应用崩溃或者是退出了...
throw new RuntimeException("Main thread loop unexpectedly exited");
}
由代码可知,在activity中,已经执行了Looper.prepareMainLooper();
方法和Looper.loop();
方法。
其中Looper.prepareMainLooper();
就等同于Looper.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));
}
注意:这里出现了一个很重要的变量sThreadLocal
,这个暂时先不讨论,之后会详细说明(问题1)。我们先看Looper
的构造函数
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
Looper
的构造函数里生成了一个消息队列(MessageQueue
)并取得了当前的线程,即主线程。
接下来我们来看looper.loop()
方法,我只取了几行关键代码:
public static void loop() {
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
for (;;) {
Message msg = queue.next(); // might block
try {
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
msg.recycleUnchecked();
}
}
先来看myLooper()
这个方法:
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
看,sThreadLocal
这个变量又出现了,前面是set(Looper)
,这里是get
返回一个Looper,也就是说sThreadLocal
这个变量存储了我们主线程的Looper对象。
另外这里有一个很重要的知识点:一个线程只有一个Looper。
可以发现,loop
方法的作用是:
- 获取当前线程的Looper对象
- 通过Looper对象获取到相应的消息队列
- 之后对消息队列进行循环
- 之后这一步就很关键啦,
msg.target.dispatchMessage(msg);
,这个我们移动到后面再讲(问题2)。
好啦,主线程的Looper相关的方法讲完了,接下来该轮到我们的主角Handler了。
三、先从Handler的构造函数谈起
Handler myHandler = new Handler()
我们同样舍弃了影响我们阅读的多余的代码;
public Handler(Callback callback, boolean async) {
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
由代码可知,Handler
的构造函数主要是获取了Looper
对象和对应的消息队列。
那好,现在问题出现了,我怎么知道我获取的这个Looper
对象就是主线程的Looper
对象呢?
四、让我们回到问题1,重新认识一下sThreadLocal
在Looper对象中:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
我们先来认识一下ThreadLocal
这个类:该类提供线程局部变量。 这些变量与其他对应类的不同之处就在于,它访问的每个线程(通过其 get或set方法)都有自己独立初始化的变量副本。
上面这段话是Google源码中的说法,什么意思呢?我们直接用源码来说明:
sThreadLocal.set(new Looper(quitAllowed));
在ThreadLocal
代码中:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
好了,到这里谜底终于揭开了,我们看到set
方法显示取了当前的线程,也就是主线程,接下来通过getmap
方法获取了主线程的ThreadLocal.ThreadLocalMap
,然后把这个Looper对象存到了主线程中。这样主线程就跟我们的Looper
对象关联起来了。
同理,在Handler
的构造函数中,我们也获取了一个Looper
对象:
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
可以看出,get
方法返回的是主线程的Looper对象。
这样一来:Handler
、Looper
和主线程也就一一对应起来了。Handler
所持有的Looper
对象就是主线程的Looper
对象。
五、Handler发送消息到消息队列
我们还是返回文章最开始的代码:
new Thread(new Runnable() {
@Override
public void run() {
//do something
Message message = Message.obtain();
message.what = 1;
myHandler.sendMessage(message);
}
}).start();
Message.obtain()
这个方法没什么可说的,它的作用是从全局池返回一个新的Message实例,允许我们在许多情况下避免分配新对象。
我们重点关注myHandler.sendMessage(message);
这个方法:
在Handler
类中,
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);
}
在这里我们看到,msg.target = this;
,this
什么,就是我们Handler
对象本身啊。我们把Handler
对象赋值给了消息的target
变量。这里有什么用呢?马上你就会知道了(问题3)。
六、Handler获取对象并执行
发送我们知道了,那么该怎么取呢?
这里我们就要返回到第二部分的looper.loop()
这个函数了,我们知道这个函数后面调用了msg.target.dispatchMessage(msg);
函数,也就是我们留在上面的问题2。
现在我们知道了,msg.target
就是我们发送消息的Handler
,接下来就是调用它的dispatchMessage(Message msg)
方法了。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
上面这个handleMessage
方法就是我们在构造Handler
时候重写的方法啦。
网友评论