//定义成static的,因为静态内部类不会持有外部类的引用
private final MyHandler mHandler = new MyHandler(this);
private static final Runnable sRunnable = new Runnable() {
@Override
public void run() {//to something}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler.postDelayed(sRunnable, 1000 * 60 * 10);
finish();
}
}
面试官:为何主线程可以new Handler?如果想要在子线程中new Handler 要做些什么准备?
小王:
每一个handler必须要对应一个looper,主线程会自动创建Looper对象,不需要我们手动创建,所以主线程可以直接创建handler。
在new handler的时候没有传入指定的looper就会默认绑定当前创建handler的线程的looper,如果没有looper就报错。
因为在主线程中,Activity内部包含一个Looper对象,它会自动管理Looper,处理子线程中发送过来的消息。而对于子线程而言,没有任何对象帮助我们维护Looper对象,所以需要我们自己手动维护。
所以要在子线程开启Handler要先创建Looper,并开启Looper循环
如果在子线程中创建了一个Handler,那么就必须做三个操作:
- prepare();
- loop();
- quit();
面试官:子线程中维护的Looper,消息队列无消息的时候的处理方案是什么?有什么用?
小王:
在Handler机制里面有一个Looper,在Looper机制里面有一个函数,叫做quitSafely()和quit()函数,这两个函数是调用的MessageQueue的quit()。
/**
- Quits the looper.
-
- Causes the {@link #loop} method to terminate without processing any
- more messages in the message queue.
-
- Any attempt to post messages to the queue after the looper is asked to quit will fail.
- For example, the {@link Handler#sendMessage(Message)} method will return false.
-
- Using this method may be unsafe because some messages may not be delivered
- before the looper terminates. Consider using {@link #quitSafely} instead to ensure
- that all pending work is completed in an orderly manner.
- @see #quitSafely
*/
public void quit() {
mQueue.quit(false);
}
/**
- Quits the looper safely.
-
- Causes the {@link #loop} method to terminate as soon as all remaining messages
- in the message queue that are already due to be delivered have been handled.
- However pending delayed messages with due times in the future will not be
- delivered before the loop terminates.
-
- Any attempt to post messages to the queue after the looper is asked to quit will fail.
- For example, the {@link Handler#sendMessage(Message)} method will return false.
*/
public void quitSafely() {
mQueue.quit(true);
}
再进入到MessageQueue的quit()函数。
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);
}
}
它会remove消息,把消息队列中的全部消息给干掉。
把消息全部干掉,也就释放了内存。
private void removeAllFutureMessagesLocked() {
final long now = SystemClock.uptimeMillis();
Message p = mMessages;
if (p != null) {
if (p.when > now) {
removeAllMessagesLocked();
} else {
Message n;
for (;😉 {
n = p.next;
if (n == null) {
return;
}
if (n.when > now) {
break;
}
p = n;
}
p.next = null;
do {
p = n;
n = p.next;
p.recycleUnchecked();
} while (n != null);
}
}
}
而在quit()函数的最后一行,有一个nativeWake()函数。
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
这个函数的调用,就会叫醒等待的地方,醒来之后,就接着往下执行。
//native的方法,在没有消息的时候回阻塞管道读取端,只有nativePollOnce返回之后才能往下执行
//阻塞操作,等待nextPollTimeoutMillis时长
nativePollOnce(ptr, nextPollTimeoutMillis);
往下执行后,发现 Message msg = mMessages; 是空的,然后就执行了这个,就接着往下走。
if (msg != null) {
…
} else {
// No more messages.
//没有消息,nextPollTimeoutMillis复位
nextPollTimeoutMillis = -1;
}
然后又调用了这个方法,并且return了null。
// Process the quit message now that all pending messages have been handled.
//如果消息队列正在处于退出状态返回null,调用dispose();释放该消息队列
if (mQuitting) {
《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整内容开源分享
dispose();
return null;
}
所以说,这个时候Looper就结束了(跳出了死循环),则达成了第二个作用:释放线程。
面试官:既然可以存在多个 Handler 往 MessageQueue 中添加数据(发消息时各个 Handler 可能处于不同线程),那它内部是如何确保线程安全的?
小王:
这里主要关注 MessageQueue 的消息存取即可,看源码内部的话,在往消息队列里面存储消息时,会拿当前的 MessageQueue 对象作为锁对象,这样通过加锁就可以确保操作的原子性和可见性了。
消息的读取也是同理,也会拿当前的 MessageQueue 对象作为锁对象,来保证多线程读写的一个安全性。
面试官:我们使用 Message 时应该如何创建它?
小王:
创建的它的方式有两种:
一种是直接 new 一个 Message 对象,
另一种是通过调用 Message.obtain() 的方式去复用一个已经被回收的 Message,
当然日常使用者是推荐使用后者来拿到一个 Message,因为不断的去创建新对象的话,可能会导致垃圾回收区域中新生代被占满,从而触发 GC。
Message 中的 sPool 就是用来存放被回收的 Message,当我们调用 obtain 后,会先查看是否有可复用的对象,如果真的没有才会去创建一个新的 Message 对象。
收的 Message,
当然日常使用者是推荐使用后者来拿到一个 Message,因为不断的去创建新对象的话,可能会导致垃圾回收区域中新生代被占满,从而触发 GC。
Message 中的 sPool 就是用来存放被回收的 Message,当我们调用 obtain 后,会先查看是否有可复用的对象,如果真的没有才会去创建一个新的 Message 对象。