前期准备 1 、2、 3 步骤
使用 4、5、6步骤
1. ActivityThread main方法(ActivityThread类中)
Looper.prepareMainLooper();// 为UI(祝线程)创建1个循环器对象
Looper.prepare() // 为当前线程(子线程)创建1个循环器对象
Looper.loop();// 开启轮循
2. prepareMainLooper() ( Looper类中)
- UI线程由prepareMainLooper函数内部调用自动生成Looper
- 子线程手动调用Looper.prepare()生成Looper对象
public static final void prepare() {
// sThreadLocal用于存储线程的变量
sThreadLocal.set(new Looper(true));
}
2.1 Looper构造函数
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
// 1. 创建1个消息队列对象(MessageQueue)
// 即 当创建1个Looper实例时,会自动创建一个与之配对的消息队列对象(MessageQueue)
mThread = Thread.currentThread();
}
3. Looper.loop();( Looper类中)
开启消息轮循
/**
* 源码分析: Looper.loop()
* 作用:消息循环,即从消息队列中获取消息、分发消息到Handler
* 特别注意:
* a. 主线程的消息循环不允许退出,即无限循环
* b. 子线程的消息循环允许退出:调用消息队列MessageQueue的quit()
*/
public static void loop() {//只贴核心代码
//1. 获取当前Looper的消息队列
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
for (;;) {
//2. next()取出消息
Message msg = queue.next(); // might block
//3. 分发消息:调用msg内部的Handler的dispatchMessage
msg.target.dispatchMessage(msg);
// 释放资源
msg.recycleUnchecked();
}
}
3.1 next()取出消息( MessageQueue类中)
next()是MessageQueue中方法
Message next() {
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// native层调用,没有消息会在这里阻塞
nativePollOnce(ptr, nextPollTimeoutMillis);
}
}
3.2 dispatchMessage分发消息 ( Handler 类中)
dispatchMessage是Handler中的方法
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
// 使用者处理消息
handleMessage(msg);
}
}
使用
4 .new Handler()
4.1 Handler构造方法做了什么事情?
- 获取当前线程Looper
- 关联MessageQueue
public Handler(Callback callback, boolean async) {
// 仅贴出关键代码
// 1. 指定Looper对象
mLooper = Looper.myLooper();
// 2. 绑定消息队列对象(MessageQueue)
mQueue = mLooper.mQueue;
// 获取该Looper对象中保存的消息队列对象(MessageQueue)
// 至此,保证了handler对象 关联上 Looper对象中MessageQueue
}
5. 创建消息对象Message
Message msg = Message.obtain()
- Message内部维护了1个Message池,用于Message消息对象的复用
- 使用obtain()则是直接从池内获取:避免每次都使用new重新分配内存
- 若池内无消息对象可复用,则还是用关键字new创建
/**
* 源码分析:Message.obtain()
* 作用:创建消息对象
* 注:创建Message对象可用关键字new 或 Message.obtain()
*/
public static Message obtain() {
// Message内部维护了1个Message池,用于Message消息对象的复用
// 使用obtain()则是直接从池内获取
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
// 建议:使用obtain()”创建“消息对象,避免每次都使用new重新分配内存
}
// 若池内无消息对象可复用,则还是用关键字new创建
return new Message();
}
6. 在工作线程中 发送消息到消息队列中
最后会都会调用enqueueMessage方法(Handler类中)
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
// 1. 将msg.target赋值为this
// 即 :把 当前的Handler实例对象作为msg的target属性
msg.target = this;
// 请回忆起上面说的Looper的loop()中消息循环时,会从消息队列中取出每个消息msg,然后执行msg.target.dispatchMessage(msg)去处理消息
// 实际上则是将该消息派发给对应的Handler实例
// 2. 调用消息队列的enqueueMessage()
// 即:Handler发送的消息,最终是保存到消息队列->>分析4
return queue.enqueueMessage(msg, uptimeMillis);
}
6.1 enqueueMessage
属于消息队列类(MessageQueue类)的方法
/**
* 定义:属于消息队列类(MessageQueue)的方法
* 作用:入队,即 将消息 根据时间 放入到消息队列中(Message ->> MessageQueue)
* 采用单链表实现:提高插入消息、删除消息的效率
*/
boolean enqueueMessage(Message msg, long when) {
synchronized (this) {
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
// 判断消息队列里有无消息
// a. 若无,则将当前插入的消息 作为队头 & 若此时消息队列处于等待状态,则唤醒
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
// b. 判断消息队列里有消息,则根据 消息(Message)创建的时间 插入到队列中
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p;
prev.next = msg;
}
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
send…和 post…区别
- 消息对象的创建 = 内部 根据Runnable对象而封装
- 发送到消息队列的逻辑 =sendM(Message msg)
常问问题
- Looper 死循环为什么不会导致应用卡死?
会阻塞在MessageQueue的next方法nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作
Handler 同步屏障
- 如何设置同步屏障?
下面 postSyncBarrier 方法创建一个MSG对象,但是MSG的target属性没有赋值
MessageQueue类:
private int postSyncBarrier(long when) {
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
// .....
return token;
}
}
Handler.postXXXX/sendXXXX,最后调用enqueueMessage时,都会为MSG的target赋值
Handler类:
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
// 为target赋值
msg.target = this;
//....
return queue.enqueueMessage(msg, uptimeMillis);
}
可以看出设置同步屏障,创建的msg,内部的target是空值。Handler发送消息最终都会在enqueueMessage中进行target赋值
MessageQueue.next()
同步屏障会在取消息中体现出来。
如果设置了同步屏障,Handler会优先处理异步消息
Message next() {
//....
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// 没有消息,进入休眠状态
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// 同步屏障,过滤掉同步消息,找到最近的异步消息
// 异步消息优先级高于同步消息
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
//....
}
}
应用场景:
Activity启动后,在绘制前中有使用到同步屏障。
ViewRootImpl类:
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
// 使用同步屏障,确保mTraversalRunnable先执行
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
//....
}
}