这是我参与8月更文挑战的第12天,活动详情查看:8月更文挑战
四个组成类各自的意义
- Message :作为消息的数据结构。(内部的几个关键字段上一篇文章已经讲过)
- MessageQueue :作为存储消息的数据结构,是一个以时间为顺序的有序列表。
- Handler: 负责消息的发送和接收
- Lopper: 开启无限循环从MessageQueue里取消息,如果取到了则执行消息内部的target.dspatchMessage(msg)
为什么我们在主线程创建Handeler没有写Looper.prepare()和Looper.loop()两个方法
为什么Android能一直运行
android.app.ActivityThread
.......
final H mH = new H();
.......
public static void main(String[] args) {
.......
Looper.prepareMainLooper();
......
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
ActivityThread 的 main 方法是一个新的 App 进程的入口,在创建ActivityThread实例之前,就执行了Looper.prepareMainLooper();
private static Looper sMainLooper;
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
prepareMainLooper内部还是执行了prepare方法,同时将主线程的sMainLooper赋值。 随后执行了Looper.loop();开启循环
主线程在app启动的main方法里执行了Looper.prepare()和Looper.loop() App能一直运行的原因是因为main方法内有一个死循环。
queue.next()什么时候为null
我们知道Looper.loop()开启了一个死循环,循环退出的唯一条件是queue.next()=null queue指的是MessageQueue,所以看看他的next啥时候返回null
# android.os.MessageQueue
Message next() {
for (;;) {
......
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
......
}
next()方法很长,但是我们只需要找return null的代码快即可。mQuitting看命令的意思是正常退出的时候。也就是说队列正在退出的时候,返回null。就意味着循环结束。再看看在什么地方将mQuitting设为true的。
忽然想想。。loop方法的结束意味着main方法的结束。app的关闭,那我们是不是想办法把主线程的消息队列的mQuitting置为false就会有意想不到的效果发生?
通过AS代码查找,可以发起全局只有一个地方执行了mQuitting = true 操作。代码如下
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
注意第一行,如果mQuitAllowed为false的时候会抛异常,大意是主线程不允许退出。
在执行MessageQueue的quit方法时,mQuitting为true。而这个方法是在Looper中触发
# android.os.Looper
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
在Looper执行quit或者quitSafely后,queue.next()返回null
Looper的构造方法什么时候穿true或false,对应的MessageQueue有什么区别
为什么main方法不会阻塞主线程
可以留意一下prepare方法这里传递的是false,我们调用Looper.prepare()的时候默认传递是true,我们还知道这个值是给MessageQueue的quit方法里,有一行 用的所以去看看有什么区别
# android.os.MessageQueue
private final boolean mQuitAllowed;
MessageQueue(boolean quitAllowed) {
赋值给了mQuitAllowed
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
在上文MessageQueue的quit方法里,执行quit的时候如果mQuitAllowed为false的时候会抛异常,大意是主线程不允许退出。
Looper构成方法传的值影响其内部的MessageQueue是否是可退出队列,只有主线程会false。所以我们主线程内也是不可以执行Looper.quit的。
为什么Loop.loop死循环不会阻塞主线程。这是因为在next方法内有一会native代码。内部做了处理。代码如下
# android.os.MessageQueue
Message next() {
......
nativePollOnce(ptr, nextPollTimeoutMillis);
......
}
一个线程能拥有几个Handler,Looper
通过上一篇文章,我们知道Looper.prepare方法只能调用一次,多次调用会报错,因为prepare方法其实是往线程本地变量存一个Looper。我们在分析Activity的时候,知道主线程有一个叫prepare的Handler。
所以一个线程可以拥有多个Handler,但是只会有一个Looper
不执行parper或多次执行parper会怎样。
不执行Looper.loop会怎样
与上面问题类似,不写Looper.prepare在创建Handlder时会抛异常,多次调用。会在调用的时候就抛异常 不执行Looper.loop则发的消息没人去取。所以不会执行任何消息
runuithread方法为什么可以执行在主线程
runuithread这个api想必大家用的很香,能在子线程中快速的切换到主线程执行代码。其实内部还是用的Handler机制
# android.app.Activity
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
代码中的mHandler是Activity中的Handler实例。运行在主线程中
runuithread方法其实就是在主线程执行了Handler.post(Runnable)而已
Handler 中使用了什么设计模式
android.os.Message
private static Message sPool;
Message next;
享元模式 主要体现在Message内部。因为sPool和next变量可以看出来是享元模式。 这里不细说,知道即可,感兴趣的可以去看看享元模式
Looper的quit和quitSafely区别
上午代码贴出来过,其实内部都是调用MessageQueue的quit方法只是参数不同。区别是quit是直接退出,quitSafely会等列表内的消息都被消费完再退出。
创建Handler的时候使用参数为Callbacl的形式
这种方式不会派生子类。(具体使用场景我也不知道。)
关于Handler还有啥问题。。暂时想不到了。欢迎补充