简介:
handler机制,在android中提供了一种异步回调机制Handler,使用它,我们可以在完成一个很长时间的任务后做出相应的通知。
每一个消息都需要被指定的Handler处理,通过Handler创建消息便可以完成此功能。Android消息机制中引入了消息池。Handler创建消息时首先查询消息池中是否有消息存在,如果有直接从消息池中取得,如果没有则重新初始化一个消息实例。典型应用就是在子线程中更新 UI 线程。那么,很显然 Handler 的作用就是用于线程间通信。
QQ截图20220606192656.pngHandler用途
Handler主要有两个主要用途:
- 在未来的某个时间点调度messages和runnables的执行
- 将要在不同线程上执行的操作加入队列
当你的应用程序被创建出来的时候,主线程会专门运行一个message queue来管理最顶级的应用对象(如activities, broadcast receivers,等等)以及它们创建的任何其它窗口。你可以创建你自己的线程,通过Handler来与主线程建立联系
Handler源码解析异步消息处理
Message next() {
...省略部分代码...
for (; ; ) {
...省略部分代码...
synchronized (this) {
Message prevMsg = null;
Message msg = mMessages;
//这里循环找异步消息 msg.isAsynchronous
if (msg != null && msg.target == null) {
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
...省略部分代码...
}
}
}
复制代码
我们来看看msg.isAsynchronous代码:
public boolean isAsynchronous() {
return (flags & FLAG_ASYNCHRONOUS) != 0;
}
static final int FLAG_ASYNCHRONOUS = 1 << 1;
复制代码
那么我们来看看在哪里添加了这个flag:
public void setAsynchronous(boolean async) {
if (async) {
flags |= FLAG_ASYNCHRONOUS;
} else {
flags &= ~FLAG_ASYNCHRONOUS;
}
}
复制代码
现在我们看到Message.setAsynchronous(boolean async)这个方法用来控制是否是异步消息,在哪里调用了呢, 我们先来看View.requestLayout():
public void requestLayout() {
...省略部分代码...
//这里的mParent是自己的父View,如果view本身已经是最顶层view,则mParent就是ViewRootImpl
if (mParent != null && !mParent.isLayoutRequested()) {
mParent.requestLayout();
}
...省略部分代码...
}
复制代码
这里会层层向上调用mParent.requestLayout(),直到调用到ViewRootImpl.requestLayout()为止
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread(); //检查当前线程
mLayoutRequested = true; //更新标记
scheduleTraversals(); //开始遍历
}
}
复制代码
void checkThread() {
//这里检查线程是否是mThread,不是的话就抛出异常,mThread一般是UI线程
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException("Only the original thread that created a view hierarchy can touch its views.");
}
}
复制代码
public boolean mTraversalScheduled;
int mTraversalBarrier;
Choreographer mChoreographer;
void scheduleTraversals() {
if (!mTraversalScheduled) { //不在执行中才执行
mTraversalScheduled = true; //更新标记为执行中
//向mHandler的MessageQueue中添加一个同步屏障,一个同步屏障也是一个Message,但是这个Message的target是null,并且后面会有一个异步消息AsynchronousMessage
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
//这里是重点!!!,通过Choreographer来post一个消息,//TAG1: CALLBACK_TRAVERSAL,要记住这个TAG
mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
...省略部分代码...
}
}
复制代码
我们先来看MessageQueue.postSyncBarrier():
public int postSyncBarrier() {
//这里使用当前时间作为参数传递进去
return postSyncBarrier(SystemClock.uptimeMillis());
}
private int postSyncBarrier(long when) {
synchronized (this) {
final int token = mNextBarrierToken++; //token增加1
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when; //msg.when就是当前时间
msg.arg1 = token; //arg1就是token
//注意!!!这个msg没有target,也就是msg.target=null
//循环遍历,将这个同步屏障msg插入到队列
Message prev = null;
Message p = mMessages;
//遍历查找when之前的消息
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
//插入到队列
if (prev != null) {
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
复制代码
再来看看removeSyncBarrier():
//这个token就是我们上面保存的
public void removeSyncBarrier(int token) {
synchronized (this) {
//遍历删除这个token对应的msg
Message prev = null;
Message p = mMessages;
//只要p.target!=null || p.arg1!=token就一直找,因为上面我们分析了同步屏障的target=null,并且arg1=token
while (p != null && (p.target != null || p.arg1 != token)) {
prev = p;
p = p.next;
}
//检测队列
if (p == null) {
throw new IllegalStateException("The specified message queue synchronization "
+ " barrier token has not been posted or has already been removed.");
}
final boolean needWake;
//移除同步屏障消息
if (prev != null) {
prev.next = p.next;
needWake = false;
} else {
mMessages = p.next;
needWake = mMessages == null || mMessages.target != null;
}
//回收
p.recycleUnchecked();
//如果需要唤醒,则唤醒等待
if (needWake && !mQuitting) {
nativeWake(mPtr);
}
}
}
复制代码
然后看mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);第一个参数是个int,第二个参数mTraversalRunnable是个Runnable,代码如下:
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
void doTraversal() {
if (mTraversalScheduled) { //不在执行中才执行
mTraversalScheduled = false; //更新标记为执行中
//从mHandler的MesssageQueue中移除同步屏障,还记得刚刚添加同步屏障的代码吗
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
//开始遍历布局
performTraversals();
}
}
//这个方法对view进行了测量,布局和绘制,我们后面会细讲
private void performTraversals() {
......
performMeasure();
......
performLayout();
......
performDraw();
......
}
复制代码
我们来看下Choreographer类的部分关键代码:
class Choreographer {
public static final int CALLBACK_INPUT = 0;
public static final int CALLBACK_ANIMATION = 1;
public static final int CALLBACK_INSETS_ANIMATION = 2;
public static final int CALLBACK_TRAVERSAL = 3; //这是刚刚的第一个参数
public static final int CALLBACK_COMMIT = 4;
private static final int CALLBACK_LAST = CALLBACK_COMMIT;
private final CallbackQueue[] mCallbackQueues;
private final FrameHandler mHandler;
private final FrameDisplayEventReceiver mDisplayEventReceiver;
private Choreographer(Looper looper, int vsyncSource) {
mLooper = looper;
mHandler = new FrameHandler(looper);
//这里初始化mDisplayEventReceiver
mDisplayEventReceiver = USE_VSYNC
? new FrameDisplayEventReceiver(looper, vsyncSource)
: null;
mLastFrameTimeNanos = Long.MIN_VALUE;
mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());
//这里对mCallbackQueues进行初始化,直接new出来一个长度为5的数组,并且初始化5个元素,为什么长度是5?因为上面说了CALLBACK_LAST=4
mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
for (int i = 0; i <= CALLBACK_LAST; i++) {
mCallbackQueues[i] = new CallbackQueue();
}
}
//这就是刚刚调用的方法
public void postCallback(int callbackType, Runnable action, Object token) {
postCallbackDelayed(callbackType, action, token, 0); //第四个参数是0
}
public void postCallbackDelayed(int callbackType,Runnable action, Object token, long delayMillis) {
if (action == null) {
throw new IllegalArgumentException("action must not be null");
}
if (callbackType < 0 || callbackType > CALLBACK_LAST) {
throw new IllegalArgumentException("callbackType is invalid");
}
postCallbackDelayedInternal(callbackType, action, token, delayMillis);
}
//最终调到这里
private void postCallbackDelayedInternal(int callbackType, Object action, Object token, long delayMillis) {
synchronized (mLock) {
final long now = SystemClock.uptimeMillis(); //获取当前时间
final long dueTime = now + delayMillis; //delayMillis = 0,所以dueTime = now
//这是个数组 callbackType是CALLBACK_TRAVERSAL,dueTime=0,action就是那个mTraversalRunnable
//这里就是将这个mTraversalRunnable保存在CALLBACK_TRAVERSAL对应的集合里了
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
if (dueTime <= now) { //true
scheduleFrameLocked(now); //跑这里
} else {
//否则通过Handler发送出去
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}
//接着看
private void scheduleFrameLocked(long now) {
if (!mFrameScheduled) {
mFrameScheduled = true;
if (USE_VSYNC) {
//看这里
if (isRunningOnLooperThreadLocked()) {
scheduleVsyncLocked();
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC); //msg.what = MSG_DO_SCHEDULE_VSYNC
msg.setAsynchronous(true); //标记为异步消息!! 前面我们加了个同步屏障了的
mHandler.sendMessageAtFrontOfQueue(msg); //发送,这个mHandler是FrameHandler
}
} else {
final long nextFrameTime = Math.max(mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);
Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, nextFrameTime);
}
}
}
}
复制代码
接着来看FrameHandler的代码:
private final class FrameHandler extends Handler {
public FrameHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_DO_FRAME:
doFrame(System.nanoTime(), 0);
break;
case MSG_DO_SCHEDULE_VSYNC: //跑到了这里
doScheduleVsync();
break;
case MSG_DO_SCHEDULE_CALLBACK:
doScheduleCallback(msg.arg1);
break;
}
}
}
复制代码
接着看
void doScheduleVsync() {
synchronized (mLock) {
if (mFrameScheduled) {
scheduleVsyncLocked();
}
}
}
private void scheduleVsyncLocked() {
mDisplayEventReceiver.scheduleVsync();
}
//来看下scheduleVsync方法:
public void scheduleVsync() {
if (mReceiverPtr == 0) {
Log.w(TAG, "Attempted to schedule a vertical sync pulse but the display event "
+ "receiver has already been disposed.");
} else {
//这里直接调用了native的方法,native调用完成会回到
nativeScheduleVsync(mReceiverPtr);
}
}
复制代码
native完事后会回调到FrameDisplayEventReceiver.onVsync()里面:
private final class FrameDisplayEventReceiver extends DisplayEventReceiverimplements Runnable {
private boolean mHavePendingVsync;
private long mTimestampNanos;
private int mFrame;
public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {
super(looper, vsyncSource, CONFIG_CHANGED_EVENT_SUPPRESS);
}
@Override
public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
long now = System.nanoTime();
if (timestampNanos > now) {
timestampNanos = now;
}
if (mHavePendingVsync) {
Log.w(TAG, "Already have a pending vsync event. There should only be "
+ "one at a time.");
} else {
mHavePendingVsync = true;
}
mTimestampNanos = timestampNanos;
mFrame = frame;
Message msg = Message.obtain(mHandler, this); //这里的第二个参数是this,意味着msg.callback = this,那么就会跑到自己的run函数
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}
@Override
public void run() {
//跑到这里了
mHavePendingVsync = false;
doFrame(mTimestampNanos, mFrame);
}
}
复制代码
void doFrame(long frameTimeNanos, int frame) {
final long startNanos;
synchronized (mLock) {
if (!mFrameScheduled) {
return; // no work to do
}
...省略计算时间的代码...
//下面开始执行事件顺序为: 1 输入事件 2 动画 3 布局
try {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
mFrameInfo.markInputHandlingStart();
doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
mFrameInfo.markAnimationsStart();
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos);
mFrameInfo.markPerformTraversalsStart();
//TAG: CALLBACK_TRAVERSAL,还记得这个TAG吗,我们看这里即可
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
} finally {
AnimationUtils.unlockAnimationClock();
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
void doCallbacks(int callbackType, long frameTimeNanos) {
CallbackRecord callbacks;
synchronized (mLock) {
//这里根据callbackType取出所有事件,这里的type是:CALLBACK_TRAVERSAL
callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(now / TimeUtils.NANOS_PER_MS);
if (callbacks == null) {
return;
}
mCallbacksRunning = true;
...省略部分代码...
}
try {
//遍历取出所有事件,执行,还记得我们之前的那个:mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null)吗?
//第二个参数就是i一个i饿runnable,就会被执行,也就是执行到我们的traversal()里去了,最终就会去测量、布局、绘制
for (CallbackRecord c = callbacks; c != null; c = c.next) {
c.run(frameTimeNanos);
}
} finally {
...回收callback...
}
}
复制代码
通过以上分析我们了解到: Android在布局的时候使用了异步消息,为什么要使用异步消息呢,有什么好处,答案就是:异步消息会被优先处理,比如当前MessageQueue中有100个消息,这时候有个布局消息来了,正常的话要等待100个执行完,如果是异步消息,则可以优先执行,这样UI就不会因为消息过多而卡顿,这就是优点,我们再来看看MessageQueue.next的代码:
Message next() {
...省略部分代码...
for (;;) {
...省略部分代码...
synchronized (this) {
final long now = SystemClock.uptimeMillis(); //记录当前时间
Message prevMsg = null;
Message msg = mMessages; //取出第一条消息,也就是队头消息
//msg.target == null! 上面分析过,这是一个同步屏障,而同步屏障后面一般会有一个人异步消息,同步屏障的目的就是为了标记后面有个异步消息的
if (msg != null && msg.target == null) { //如果有同步屏障,那么就找异步消息
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous()); //果然在这里,只要不是异步消息,就一直找,直到找到为止
}
//开始处理消息
if (msg != null) {
...处理msg...
} else {
// 标记为-1无限等待
nextPollTimeoutMillis = -1;
}
...省略部分代码...
}
...省略处理空闲消息的代码...
}
}
复制代码
上面省略了部分代码,主要分析了 异步消息是被优先执行的,而且异步消息都跟在同步屏障后面的
1.为什么要把Looper放在ThreadLocal里面
我们先来看怎么把Looper放在ThreadLocal里面的? Looper.prepare()
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<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));
}
复制代码
ThreadLocal.set()
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
复制代码
ThreadLocalMap.getMap() 可以看到Thread有一个成变量ThreadLocalMap
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
public class Thread implements Runnable {
ThreadLocal.ThreadLocalMap threadLocals = null;
}
复制代码
createMap() 这里直接创建一个ThreadLocalMap
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
复制代码
这里来总结一下存放Looper的逻辑
- 1 每个线程都有个ThreadLocalMap的成员变量threadLocals,这个变量可以理解为一个HashMap,其中key是ThreadLocal,value是任意类型。
- 2 Looper内部有个ThreadLocal静态成员变量sThreadLocal,在prepare()的时候,会先创建一个Looper,然后获取当前线程的ThreadLocalMap,并且把自自己的成员变量sThreadLocal作为key,把这个looper作为值存放进去,可以简单理解为:
Looper looler = new Looper(); //创建一个looper
ThreadLocalMap map = Thread.currentThread().getMap(); //获取当前线程的ThreadLocalMap
map.set(sThreadLocal, looper); //将looper存入当前线程的ThreadLocalMap中
复制代码
我们再来看看是怎么从ThreadLocal里面获取Looper的?
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
复制代码
很简单,直接使用ThreadLocal的get()函数,这里可以缕一下: 因为ThreadLocalMap是每个线程都有的成员变量,所以在线程A里面使用Looper.myLooper()得到的就是线程A的looper,线程B得到的就是线程B的looper,UI线程肯定就是UI线程的looper,所以就达到了线程切换的目的。
2.ActivityThread.main()是在哪里被调用的
答: 是在app进程被创建的时候调用的,app进程被创建的时候,会通过zygote来fork一个新进程作为app进程,然后通过反射调用ActivityThread的main方法,详细流程后面我们专门出一个章节来讲
总结
异步消息跟在同步屏障后面(不一定是紧临),异步消息会被优先执行,寻找异步消息的过程是先找同步屏障,如果有,就遍历next来找异步消息,同步屏障的特点是msg.target=null,异步消息的特点是isAsynchronous = true。 eg: 同步屏障是50,异步消息是100,同步屏障前面的1-49会先执行,然后执行到50的时候发现是同步屏障,然后就遍历后续消息,找到异步消息100,取出来执行,然后移除同步屏障50,在继续执行后面的消息51,以此类推
使用ThreadLocal是为了将Looper保存在线程内部,从而达到跨线程的目的
网友评论