Handler,MessageQueue,Looper这四大金刚,
- Message : 消息载体 ;
- Handler : 发送和处理消息;
- MessageQueue :存储消息;
- Looper:传输消息(MessageQueue —> Handler) ;
1. Looper的功能
Handler究其本质就是用来实现线程间通信。在Android开发过程中,我们通常在子线程(Thread) 中将消息发送给 主线程(MainThread), 而 主线程(MainThread) 需要去获取到消息,进而才能去处理消息。
谁来保证主线程(MainThread)获取到消息呢?
Looper,舍我其谁…
源码中对于Looper类的解释,已经很详细啦。
Class used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, call {@link #prepare} in the thread that is to run the loop,and then {@link #loop} to have it process messages until the loop is stopped.
该类用于为一个线程执行消息轮询。默认情况下线程是没有用于消息轮询的looper的,需要在消息轮询的线程中调用prepare()方法来创建一个Looper对象,然后调用loop()去处理消息直到轮询停止。
因为处理消息的线程是只负责处理消息的,压根不知道消息什么时候来。所以才需要Looper不停的刺探军情,为线程去轮询消息。
2.Looper的使用前提
在Handler的前世今生1——ThreadLocal 谈到TheadLocal来保证Thread和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));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
一个线程只有一个Looper
一个Looper只有一个MessageQueue.
Thread : Looper : MessageQueue = 1 : 1 : 1
显而易见,Looper 从其对应的MessageQueue 中去轮询消息
2.1 MainLooper
在开发过程中,有时候我们会通过getMainLooper() 来切换到UI线程上。而且我们在主线程中使用Handler时,并没有调用prepare() 和 loop() 等方法。这中间发生了什么有趣的事情呢?
这就需要我们去查看ActivityThread的main()方法。
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
// Install selective syscall interception
AndroidOs.install();
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
//1. 调用了Looper的prepare()方法
Looper.prepareMainLooper();
// Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
// It will be in the format "seq=114"
long startSeq = 0;
if (args != null) {
for (int i = args.length - 1; i >= 0; --i) {
if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
startSeq = Long.parseLong(
args[i].substring(PROC_START_SEQ_IDENT.length()));
}
}
}
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
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");
}
可以看出来,在Looper的使用方法上没有什么区别。
- prepareMainLooper()方法
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
- getMainLooper()方法
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
在Looper里面,我们看到了synchronized + static的实际应用啦。
我们应该理解,
- 这里保持同步,因为它是主线程,肯定不能在多线程情况下出现问题;
- prepare(false),false 是因为主线程不允许退出,除非应用退出。
3. Looper的loop()
上面讲了那么多,但是都没有切入到Looper的功能。loop()方法才是整个Looper类的核心。调用loop()才表示真正的发车啦。
实际上,我们都说Looper是负责消息轮询的,若没有消息则阻塞,该阻塞的逻辑实现是在MessageQueue的next()方法。以后会展开讨论…
public static void loop() {
// 1. 取到looper对象
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// 2. 获取MessageQueue
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();
// 3. 无限轮询
for (;;) {
// 这里会阻塞,一定要记住,阻塞逻辑是在MessageQueue中实现的。而不是这里
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// Make sure the observer won't change while processing a transaction.
// Observer就是一个监听事件发送过程的接口
final Observer observer = sObserver;
Object token = null;
if (observer != null) {
token = observer.messageDispatchStarting();
}
long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
try {
// 重点在这里,此处的target其实就是Handler
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
} finally {
ThreadLocalWorkSource.restore(origWorkSource);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
// 消息复用机制
msg.recycleUnchecked();
}
}
3.1. loop()的流程
- 获取Looper对象,然后拿到MessageQueue;
- 开启轮询,
2.1. 通过MessageQueue 的next() 获取消息;
2.2. 表面上,若获取的消息为空,则退出循环(可能退出循环吗?不可能的);
2.3. 通过Message.target(即Handler),调用 dispatchMessage(msg) 进行消息分发和处理。
2.4 调用Message 的recycleUnchecked() ,进行消息回收。
再次强调:loop()方法没有消息则阻塞,阻塞的逻辑是在MessageQueue的next()实现的。
4. Looper还有退出功能
Looper在没有消息的时候进行阻塞,终归还是要消耗一部分资源的。所以除了主线程,其他线程还是需要退出循环功能的。
PS:值得我们注意的是,退出功能最终也是交由MessageQueue实现的。
public void quit() {
mQueue.quit(false);
}
推荐使用quitSafely()
public void quitSafely() {
mQueue.quit(true);
}
至于两者的区别,跟线程池的退出机制类似…
5. 关于loop()方法的考点
5.1 loop()死循环为什么没有阻塞主线程?
我们会看到ActivityThread的Looper.loop()方法是在main()方法的最后。而Android本身就是靠事件驱动的。所以Looper.loop()不断地接收并处理消息。我们的代码都是在这个循环中执行的,所以当然不会阻塞啦。具体可以自行百度。
5.2 loop()没有消息时是如何处理的?
其实还是想强调一下:loop()的阻塞逻辑是在MessageQueue中实现的。
6. 总结
Looper只是用来负责消息轮询的,与负责发送和接收处理消息的Handler是没有任何关联的。这一点也可以通过源码来证实。涉及到的对应关系如下:
Looper : Thread : MessageQueue = 1 : 1 : 1
Looper : Handler = 1 : N
Looper 和 Handler是没有任何联系的。
Looper 本身的阻塞功能和退出功能都是MessageQueue实现的。
欢迎大家继续收看 Handler的前世今生3——Message