- handler线程安全的
- handler怎么用
- post(Runnable)
- postDelayed(Runnable,long);
- sendMessage
- sendMessageDelayed
参考文章
Android UI线程ActivityThread.main会默认创建一个looper,默认调用looper.prepare和looper.loop方法
如果想在子线程中建立Handler来处理业务,需要手动先调用Looper.prepare方法,之后创建Handler,最后在调用Looper.loop方法。
实例代码
// 创建一个子线程,并在子线程中创建一个Handler,且重写handleMessage
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
subHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// 处理消息
switch (msg.what) {
case MSG_MAIN_TO_SUB:
Log.e(TAG, "接收到消息: " + Thread.currentThread().getName() + ","+ msg.obj);
break;
default:
break;
}
}
};
Looper.loop();
}
}).start();
这样可以实现从主线程向子线程发送消息。
Looper:一个线程最多只存在一个Looper对象,如果没有,去创建一个Looper,并存放在sThreadLocal中。
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
// 规定了一个线程只有一个Looper,也就是一个线程只能调用一次Looper.prepare()
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 如果当前线程没有Looper,那么就创建一个,存到sThreadLocal中
sThreadLocal.set(new Looper(quitAllowed));
}
Looper会创建一个MessageQueue对象
private Looper(boolean quitAllowed) {
// 创建了MessageQueue,并供Looper持有
mQueue = new MessageQueue(quitAllowed);
// 让Looper持有当前线程对象
mThread = Thread.currentThread();
}
MessageQueue存放Message
Looper.loop
public static void loop() {
// 得到当前线程的Looper对象
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// 得到当前线程的MessageQueue对象
final MessageQueue queue = me.mQueue;
// 无关代码
......
// 死循环
for (;;) {
// 不断从当前线程的MessageQueue中取出Message,当MessageQueue没有元素时,方法阻塞
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// Message.target是Handler,其实就是发送消息的Handler,这里就是调用它的dispatchMessage方法
msg.target.dispatchMessage(msg);
// 回收Message
msg.recycleUnchecked();
}
}
Message信息携带者,一个线程可以有多个
Handler发送消息以及消息的回调实现,一个线程可以有多个Handler
Looper消息遍历者,从MessageQueue中循环取出Message进消息行处理,一个线程只有一个
MessageQueue消息队列,存放Handler发送消息,供Looper循环取消息,一个线程只有一个
sendMessageDelayed方法最终都是会被调到,只是延迟时间可能为0,
Handler相关面试问题
- Looper.loop可以停止吗
答:quit和quitSafely方法停止looper - Android中,有哪些是基于Handler来实现通信的?
答:App的运行、更新UI、AsyncTask、Glide、RxJava等 - 处理Handler消息,是在哪个线程?一定是创建Handler的线程么?
答:创建Handler所使用的Looper所在的线程 - 消息是如何插入到MessageQueue中的?
答: 是根据when在MessageQueue中升序排序的,when=开机到现在的毫秒数+延时毫秒数 - 当MessageQueue没有消息时,它的next方法是阻塞的,会导致App ANR么?
答:不会导致App的ANR,是Linux的pipe机制保证的,阻塞时,线程挂起;需要时,唤醒线程 - 子线程中可以使用Toast么?
答:可以使用,但是Toast的显示是基于Handler实现的,所以需要先创建Looper,然后调用Looper.loop。 - Looper.loop()是死循环,可以停止么?
答:可以停止,Looper提供了quit和quitSafely方法 - Handler内存泄露怎么解决?
答: 静态内部类+弱引用 、Handler的removeCallbacksAndMessages等方法移除MessageQueue中的消息
网友评论