几个关于Handler 的思考问题
Handler 消息管理机制,如何管理事务?
Handler在线程间如何通信?
Handler 内存共享方案
架构简图
->「主线程,消费者」queue.next
[Message] <--------[Looper] ===>[dispatchMessage]===>[handleMessage]
[Message]
[Message]
[Message]
[Message]
.
.
.
[Message] <-----queue.enqueueMesage-----[Handler]
->「子线程,生产者」
我们使用时候的过程
子线程(bean)-> 主线程(显示)
涉及到的知识点
ActiviyThread->AMS 流程
ActivitThead 里面的main()方法
我们的应用启动过程
Launcher(app):zygote->jvm->ActivityThread.main()==>[Looper.prepareMainLooper() ------looper.loop]
设计思路,handler 的message消息队列为什么不阻塞?
这是因为整个系统都再用handler 的message机制。如果阻塞会产生手机卡死的情况。
设计模式,是一种生产折消费者常用模式,生产--->缓存池----->消费
生产者,通过enqueueMessage ----->[MessageQueue 仓库]-----> next() 取出消息,消费者。
异步消息,同步消息
看完这个链接,或者看完本文
消息屏障/handlerThread IntentService
看完这个链接,或者看完本文
参考掘金: https://juejin.im/post/6844903910113705998
MessageQueue特殊情况
两个方面的阻塞:
- 消息没到执行时刻,会根据when 阻塞,计算nextpollTimeout---nativePollOnce
自动唤醒
2.消息队列为空的时候 nativePollOnce---ptr -nativeInt()
无限等待状态。
当其他消息进来就会唤醒
源码分析
开始
使用的入口
Hndler=>
「无论调用那个postAtTime 最后会执行」
「无论调用那个sendMessage最后会执行」
=>sendMessageDelayed(Message msg, long delayMillis)
=> sendMessageAtTime(Message msg, long uptimeMillis)
「uptimeMills====SystemClock.uptimeMillis() + delayMillis 系统时间+我们传入的延时时间 ,在那个时间点执行」
=>enqueueMessage(queue, msg, uptimeMillis)
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
传送带功能
MessageQueue跟随Looper 顺带创建的
MessageQueue数据结构
单链表实现的优先级队列(根据时间条件优先级判断)
根据时间优先级,是一个插入排序算法,优先级队列是取消息是从队列头开始,
MessageQueue 持有 (Message mMessages;)
链表 Message->next->Message->next
Message => 持有(Handler target;)注意这个是Message 持有target相当于Handler自身this,由于消息发送可能延时,我们Activity声明周期结束,但是内部类持有外部引用,造成内存泄露的原因之一
MessageQueue 的 mQuitting默认false调用quit ---true
quit清空消息,调用本地nativeWake唤醒
下面的Looper
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);
}
}
MessageQueue 的 synchronized (this)
内置锁保证安全
一个线程只有一个MessageQueue,而且MessageQueue还有内置对象锁所以保证唯一
boolean enqueueMessage(Message msg, long when) {
->「注意这里target」
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
->「注意这里mQuitting」
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
->「如果消息队列有消息,判断节点p,根据我要加入消息when比p时间早,轮询判断进行插入」
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
->「当消息为空的时候,再次来其他消息,需要唤醒」
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
轮询取消息
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
->「nativePollOnce 承接上文唤醒队列」
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
->「 如果target==null,那么它就是屏障,需要循环遍历,一直往后找到第一个异步的消息
」
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
我们有很多native方法。系统实现了很多JNI层
nativePollOnce(睡眠)/nativeWake 唤醒操作
底层调用epoll方法,使线程阻塞
private native static long nativeInit();
private native static void nativeDestroy(long ptr);
@UnsupportedAppUsage
private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/
private native static void nativeWake(long ptr);
private native static boolean nativeIsPolling(long ptr);
private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
动力
Looper 相关
-> Looper 的初始化
构造函数是私有的,不能随便创建
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
持有一个引用final MessageQueue mQueue;
我们的MessageQueue 就是在这里顺带创建的
在prepare里面进行的初始化
private static void prepare(boolean quitAllowed) {
->「注意承接上面,保证唯一行threadLocal get 检查」
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
所有的当前线程的Looper myLooper方法,唯一性
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
Looper 的 ThreadLocal
初始化中
static final ThreadLocal
上下文存储变量,内部有一个ThreadLocalMap k 就是threadlocal 本身,而他是final的 ,每次set
是一个常量保证了唯一性
ThreadLocal 维护类自己的Map
static class ThreadLocalMap {
static class Entry extends WeakReference> {
Object value;
Entry(ThreadLocal> k, Object v) {
super(k);
value = v;
}
}
...
}
一个线程----》只有一个Looper 不能改
一个线程----》ThreadLocalMap---》<唯一的ThreadLocal,value>
内部维护原子性操作
private static AtomicInteger nextHashCode =
new AtomicInteger();
prepare 的时候先get 判断是否存在
Looper loop()
执行流程
looper.loop()->messageQueue.next()->handler.dispatchMessage->handler.handlerMessage()
承接Message
「msg.target 相当于====handler===dispatchMessage 回调」
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
// Allow overriding a threshold with a system prop. e.g.
// adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
final int thresholdOverride =
SystemProperties.getInt("log.looper."
+ Process.myUid() + "."
+ Thread.currentThread().getName()
+ ".slow", 0);
boolean slowDeliveryDetected = false;
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
// Make sure the observer won't change while processing a transaction.
final Observer observer = sObserver;
final long traceTag = me.mTraceTag;
long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
if (thresholdOverride > 0) {
slowDispatchThresholdMs = thresholdOverride;
slowDeliveryThresholdMs = thresholdOverride;
}
final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);
final boolean needStartTime = logSlowDelivery || logSlowDispatch;
final boolean needEndTime = logSlowDispatch;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
final long dispatchEnd;
Object token = null;
if (observer != null) {
token = observer.messageDispatchStarting();
}
long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
try {
->注意留意「msg.target」
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
} finally {
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logSlowDelivery) {
if (slowDeliveryDetected) {
if ((dispatchStart - msg.when) <= 10) {
Slog.w(TAG, "Drained");
slowDeliveryDetected = false;
}
} else {
if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
msg)) {
// Once we write a slow delivery log, suppress until the queue drains.
slowDeliveryDetected = true;
}
}
}
if (logSlowDispatch) {
showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
结束
Handler 的Callback
Handler->handlerMessage()
共享内存
整个过程是Message在动的过程
Message产生,new Message() 或者 obtain过来,所以就体现子线程到主线程的内存共享
内存不分线程,所以子线程能用,主线程也能用。
也是为什么不能不断new Message的原因
退出和空消息
如果子线程调用 消息为空如何处理
需要主动调用quite,Looper 的loop 是个无限循环,msg == null
quit-》唤醒-》messagequeue = null -> 退出Looper
quit函数---》nativeWake()
Handler 内存泄露的原因和解决
- Activity 使用错误,使用了匿名内部类。默认会持有 this,handler 持有外部类Activity。-----------解决改成static 静态内部类
Handler handler = new Handler(){
......
}
- 其他的内部类持有为什么没有这种问题,recyclerview 的Adapter 的viewholder?声明周期的原因
sendMessageAtTime--->enqueueMessage{
msg.target = this;=====handler
}
整个message传递过程,需要延迟处理,message持有handler,handler持有activity。GC无法回收,可达性算法不能回收
------------------解决 在 onDestroy的时候 主动调用removeCallbacksAndMessages 释放
HandlerThread是什么?为什么它会存在?
HandlerThread是Thread的子类,严格意义上来说就是一个线程,只是它在自己的线程里面帮我们创建了Looper HandlerThread 存在的意义如下:
- 方便使用:a. 方便初始化,b,方便获取线程looper 2)保证了线程安全
我们一般在Thread里面 线程Looper进行初始化的代码里面,必须要对Looper.prepare(),同时要调用Loop。 loop();
@Override
public void run() { Looper.prepare(); Looper.loop();
}
而我们要使用子线程中的Looper的方式是怎样的呢?看下面的代码
Thread thread = new Thread(new Runnable() {
Looper looper;
@Override
public void run() {
// Log.d(TAG, "click2: " + Thread.currentThread().getName());
Looper.prepare();
looper =Looper.myLooper(); Looper.loop();
}
public Looper getLooper() {
return looper;
} });
thread.start();
Handler handler = new Handler(thread.getLooper());
上面这段代码有没有问题呢? 肯定有
1)在初始化子线程的handler的时候,我们无法将子线程的looper传值给Handler,解决办法有如下办法:
a. 可以将Handler的初始化放到 Thread里面进行
b. 可以创建一个独立的类继承Thread,然后,通过类的对象获取。
这两种办法都可以,但是,这个工作 HandlerThread帮我们完成了
2)依据多线程的工作原理,我们在上面的代码中,调用 thread.getLooper()的时候,此时的looper可能还没有初 始化,此时是不是可能会挂掉呢?
以上问题
HandlerThread 已经帮我们完美的解决了,这就是 handlerThread存在的必要性了。
我们再看 HandlerThread源码
public void run() {
mTid = Process.myTid(); Looper.prepare(); synchronized (this) {
mLooper = Looper.myLooper();
notifyAll(); //此时唤醒其他等待的锁,但是 }
Process.setThreadPriority(mPriority); onLooperPrepared();
Looper.loop();
mTid = -1;
}
它的优点就在于它的多线程操作,可以帮我们保证使用Thread的handler时一定是安全的。
IntentService 默认创建HandlerThread 参见 https://www.jianshu.com/p/ad5613d3955e
应用场景1:
IntentService 应用:本身继承service: 处理后台耗时任务
处理完》 IntentService 自动停止:内存释放
应用需求:一项任务分成几个子任务,子任务按顺序执行,子任务全部执行完成后,这项任务才算成功
这个需求可以用多个线程来处理,一个线程处理完 -> 下一个线程 -> 下一个线程
IntentService就可以帮助我们完成这个工作,而且,能够很好的管理线程,保证只有一个线程处理工作,而且是一个一个的完成任务,有条不紊的进行
同一个线程-》顺序执行1 2 3 4 : 对线程的控制么
原因:
IntentService 每个HandlerThread 维护一个Looper 保证looper绑定
每个任务是一个消息,维护了一个队列messageQueue
先来后到 保证线程消息一个一个执行。
类似的场景2:缓存操作参见
Messagequeue 队列处理机制在fragment生命周期管理中的应用,glide
https://www.jianshu.com/p/317b2d6bde1b
题外话:我们在Glide里面handler的场景
Glide.with(context).from(url).into(iamgeView)
我们通常context 缩小范围,如果默认用子线程他会默认给你转成applicationContext容易造成泄露
context:? fragment.getAppalicationContext
RequestManagerFragment getRequestManagerFragment(final android.app.FragmentManager fm) {
pendingRequestManagerFragments = new HashMap();
//尝试根据id去找到此前创建的RequestManagerFragment
RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
//如果没有找到,那么从临时存储中寻找
current = pendingRequestManagerFragments.get(fm);
if (current == null) {
//如果仍然没有找到,那么新建一个RequestManagerFragment,并添加到临时存储中。
//然后开启事务绑定fragment并使用handler发送消息来将临时存储的fragment移除。
current = new RequestManagerFragment();
pendingRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
场景3:Fragment的声明周期管理,也用到了MessageQueue和handler相关东西。
android View中的Looper Handler message
Choreographer 屏幕的点击 「翻译过来是舞者」
同步屏障,保证不掉帧 vsync
我们现在手机最低60hz ,也就是刷新率1000ms/60帧率=16帧/ms
消息机制之同步屏障
线程的消息都是放到同一个MessageQueue里面
,取消息的时候是互斥取消息
,而 且 ,而添加消息是按照消息的执行的先后顺序
进行的排序,那么问题来了,同一个时间范围内的消 息,如果它是需要立刻执行
的,那我们怎么办,按照常规的办法,我们需要等到队列轮询到我自己的时候才能执行 哦,那岂不是黄花菜都凉了。所以,我们需要给紧急需要执行的消息一个绿色通道,这个绿色通道就是同步屏障的概 念
。
同步屏障是什么?
屏障的意思即为阻碍,顾名思义,同步屏障就是阻碍同步消息,只让异步消息通过
。如何开启同步屏障呢?如下而 已:
MessageQueue#postSyncBarrier()
源代码是这样
/**
* Posts a synchronization barrier to the Looper's message queue.
*
* Message processing occurs as usual until the message queue encounters the
* synchronization barrier that has been posted. When the barrier is encountered,
* later synchronous messages in the queue are stalled (prevented from being executed)
* until the barrier is released by calling {@link #removeSyncBarrier} and specifying
* the token that identifies the synchronization barrier.
*
* This method is used to immediately postpone execution of all subsequently posted
* synchronous messages until a condition is met that releases the barrier.
* Asynchronous messages (see {@link Message#isAsynchronous} are exempt from the barrier
* and continue to be processed as usual.
*
* This call must be always matched by a call to {@link #removeSyncBarrier} with
* the same token to ensure that the message queue resumes normal operation.
* Otherwise the application will probably hang!
*
* @return A token that uniquely identifies the barrier. This token must be
* passed to {@link #removeSyncBarrier} to release the barrier.
*
* @hide
*/
@TestApi
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
Message prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
可以看到,Message 对象初始化的时候并没有给 target 赋值,因此, target == null 的 来源就找到了。上面消 息的插入也做了相应的注释。这样,一条target == null 的消息就进入了消息队列。
那么,开启同步屏障后,所谓的异步消息又是如何被处理的呢? 如果对消息机制有所了解的话,应该知道消息的最终处理是在消息轮询器 Looper#loop() 中,而 loop() 循环中会
调用 MessageQueue#next() 从消息队列中进行取消息。
->上面的「传送带」Message next() 查看源码
从上面可以看出,当消息队列开启同步屏障的时候(即标识为msg.target == null),消息机制在处理消息的时 候,优先处理异步消息。这样,同步屏障就起到了一种过滤和优先级的作用。
同步消息的应用场景
似乎在日常的应用开发中,很少会用到同步屏障。那么,同步屏障在系统源码中有哪些使用场景呢?Android 系统中
的 UI 更新相关的消息即为异步消息,需要优先处理。
比如,在 View 更新时,draw、requestLayout、invalidate 等很多地方都调用了
ViewRootImpl#scheduleTraversals() ,如下:
//ViewRootImpl.java
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
-》【开启同步屏障】
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
-》【发送异步消息】
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
postCallback() 最终走到了
Choreographer-》postCallbackDelayedInternal() :
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
if (DEBUG_FRAMES) {
Log.d(TAG, "PostCallback: type=" + callbackType
+ ", action=" + action + ", token=" + token
+ ", delayMillis=" + delayMillis);
}
synchronized (mLock) {
final long now = SystemClock.uptimeMillis();
final long dueTime = now + delayMillis;
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
if (dueTime <= now) {
scheduleFrameLocked(now);
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
-》【异步消息】
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}
这里就开启了同步屏障,并发送异步消息,由于 UI 更新相关的消息是优先级最高的,这样系统就会优先处理这些异 步消息。
最后,当要移除同步屏障的时候需要调用 ViewRootImpl#unscheduleTraversals() 。
void unscheduleTraversals() {
if (mTraversalScheduled) {
} }
小结
同步屏障的设置可以方便地处理那些优先级较高的异步消息。当我们调用
Handler.getLooper().getQueue().postSyncBarrier() 并设置消息的 setAsynchronous(true) 时,target 即 为 null ,也就开启了同步屏障。当在消息轮询器 Looper 在 loop() 中循环处理消息时,如若开启了同步屏障,会优 先处理其中的异步消息,而阻碍同步消息。
享元设计模式(内存复用)
Message
obtain() 用的一个对象池
public static final Object sPoolSync = new Object();
message 用的是对象池
当结束后,会回收
点击事件会转换成message发送出去
Looper 的 loop死循环 跟ANR 没有关系,只是ANR发生 5s handler会发出提醒
ActivityThread的 main 方法的主要作用就是做消息循环,一旦退出消息循环,主线程运行完毕,那么你的应用也就退出了。Android是事件驱动的,在Looper.loop()中不断接收事件、处理事件,而Activity的生命周期都依靠于主线程的 Loop.loop() 来调度,所以可想而知它的存活周期和 Activity 也是一致的。当没有事件需要处理时,主线程就会阻塞;当子线程往消息队列发送消息,并且往管道文件写数据时,主线程就被唤醒。真正会卡死主线程的操作是在执行回调方法 onCreate/onStart/onResume 等操作的时间过长,导致掉帧,甚至发生ANR,looper.loop() 本身不会导致应用卡死。
主线程Looper从消息队列读取消息,当读完所有消息时,主线程阻塞。子线程往消息队列发送消息,并且往管道文件写数据,主线程即被唤醒,从管道文件读取数据,主线程被唤醒只是为了读取消息,当消息读取完毕,再次睡眠。因此loop的循环并不会对CPU性能有过多的消耗。