代码里部分含有中文注释,也主张认真看一下。当然源码的英文注释也十分有帮助
本文合适对Handler的运用根本了解,想要深化探索其运转机制和源码原理的人群,初学者主张先阅览学习 Android中Handler的根本运用
鉴于内容较多,后续扩展较广,主张依据需求,分批分段阅览,或时常复读,常读常新。愿对加深你的了解有帮助
Handler
先看Handler主要的结构办法和成员变量:Looper、MessageQueue是比较要害的
这儿得到对于一个Looper目标,能够实例化n个Handler与之绑定。刚开端这儿可能还比较懵,先看后面,这儿留个疑问吧:为什么Handler需求与Looper绑定
final Looper mLooper;
final MessageQueue mQueue;
final Callback mCallback;
// 这个仍是比较眼熟的吧,便是实例化Handler一般会传入的,或许会直接重写本身的handleMessage办法
public interface Callback {
/**
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
boolean handleMessage(@NonNull Message msg);
}
// 这两个能够先不看,主张整体知识能够串联起来后再自主回忆一遍
final boolean mAsynchronous;
IMessenger mMessenger;
// 推荐的Handler的获取办法需求传入Looper目标,关于Looper的获取请看对应阶段
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
mLooper = looper;
// 其间MessageQueue来自于Looper
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
Handler一般怎样运用
一般运用handler,会实例化一个 Meaasge目标,然后经过 sendMessage 办法发射出去,在重写的 handleMessage 办法或传入的Callback接口完成中处理回调内容,关于回调怎样运用,这儿就不再阐述了。
// 当然对它自带的分发机制不满意的话,也能够override
public void dispatchMessage(@NonNull Message msg) {
// 这儿发现Message也有callback,而且是个Runnable,是个互斥逻辑
// 这儿的callback是调用Handler的post办法时,绑定在Message目标上的
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
// 包含这儿,Callback的完成的回来值假如为true,有点onTouchEvent消费事情的意味,将会阻拦重写的Handler的handleMessage办法
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
在时序图中能够看出大致的调用链,终究会调用 enqueueMessage 结合代码咱们发现,不管是post、Delayed、AtTime等变体办法,终究实际是将时刻核算为产生时刻uptimeMillis,结合办法名应该是放入了MessageQueue的音讯行列中。 Handler的本身先告一阶段,后面跟着Looper和MessageQueue的解析再继续品吧
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
// 这儿十分要害!!!
// 这儿对Message目标绑定了target,this也便是Handler本身,这也是为什么后面能收到回调的原因,相当于常用的观察者形式的接口持有
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
// 这儿设置了是否为异步形式,这儿需求结合同步屏障一同食用,能够先不看
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
MessageQueue
首要望文生义,是个保护Message的行列,经过持有头节点掌握行列。内部经过next指针形成链,一起在新节点刺进时,会遵从when的时刻优先
Message mMessages;
public final class Message implements Parcelable {
// 一般用于区分音讯,即类似于通讯code
public int what;
// enqueueMessage的时分传入的uptimeMillis
public long when;
// 存储数据的当地
Bundle data;
// handler绑定持有
Handler target;
// handler.post会把履行的action存在这,终究在dispatch的时分调用
Runnable callback;
Message next;
}
MessageQueue的入队
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
// target为空,就会抛反常,所以前面Handler的enqueueMessage中绑定target十分重要
throw new IllegalArgumentException("Message must have a target.");
}
synchronized (this) {
if (msg.isInUse()) {
// 这儿能够留个心眼,重视下Message的状况什么时分会改变
throw new IllegalStateException(msg + " This message is already in use.");
}
if (mQuitting) {
// 调用了quit办法后,mQuitting才为true
// Looper持有了MessageQueue,能够调用该办法,供给了安全和不安全两种办法
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;
}
// 看,这儿变更了Message的状况,其间的标志位是二进制位运算进行保护的
msg.markInUse();
// 之前核算出来的uptimeMillis被存在了Message目标的when特点中
msg.when = when;
// p便是一个header节点,Message内部经过next指针形成链表
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// 这儿就对头节点重新赋值,假如为空或 比当时头节点履行要早
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
// 当MessageQueue为空且没有idleHandler要处理,mBlocked为true
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为true需求满意以下几个条件
// 堵塞标志位mBlocked为true,后面会剖析
// 头结点target为空,所以想要突破同步屏障,需求防止几个设置target的办法,或许手动置空
// mAsynchronous需求为true
// 暂时先了解这些
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
// 循环遍历,prev会到合适刺进的节点
prev = p;
p = p.next;
if (p == null || when < p.when) {
// 这儿还有时刻的比较,所以是时刻优先行列
break;
}
if (needWake && p.isAsynchronous()) {
// needWake会在这儿被消费掉,取决于p是否是异步音讯
// 由于之前唤醒过了,就不需求再唤醒了
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;
}
MessageQueue取音讯
这儿还有个比较重要的办法 next() ,由于已知MessageQueue是一个保护Message的行列,那行列需求出队的当地,就在这儿
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) {
// 当行列遍历时发现mQuitting,即被调用了quit后,ptr即为0
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
// 一个死循环,意味着一定会回来一条音讯,否则则会堵塞,直到新音讯到来
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// 进行堵塞休眠,与enqueueMessage中的nativeWake唤醒对应
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;
if (msg != null && msg.target == null) {
// 这儿就描绘了同步屏障,所以需求记住同步屏障的条件便是target为空
// 假如需求了同步屏障,就会遍历去找到下一个异步音讯,由于同步的会被堵塞
// 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;
}
// 假如前面找到了合适的Message,就直接return了,这儿就走不进来,所以现在的状况是IDLE的搁置挂起,或许进行了安全退出或退出时,后续没有音讯了
// 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) {
// 有IdleHandler就履行,没有那就堵塞标志位mBlocked启用
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
// mPendingIdleHandlers初始化懒加载
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
// mIdleHandlers copy一份到mPendingIdleHandlers,由于mPendingIdleHandlers中取出即会置空
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 {
// 调用IdleHandler接口的queueIdle,并依据其回来值决议是否remove掉
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
// mPendingIdleHandlers仅仅一个暂时行列,而mIdleHandlers才用来持久维持,需求keep的则不会被remove,下次还会被copy过来
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;
}
}
Looper
Looper是怎样来的?
Looper在结构中会绑定当时线程,而且会实例化一个MessageQueue 所以MessageQueue和Looper是1:1联络
final MessageQueue mQueue;
final Thread mThread;
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
结构是私有的,该怎样调用呢?
只有 prepare 办法供给了结构的调用,也便是说想要获取Looper,先得调用Looper.prepare 实例化后存入ThreadLocal且不能屡次调用,否则会抛反常
public static void prepare() {
prepare(true);
}
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));
}
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
这儿附了ThreadLocal的set办法,帮助咱们了解。 ThreadLocal能够实例化多个,但终究都会归到 所属Thread的ThreadLocalMap目标,即每个线程都有的一个本地备份,且相互之间不搅扰
所以得到,对应联络和层级持有联络如先后次序所示
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
为什么Handler实例化的时分,不传Looper也能够?
尽管这个办法现已被弃用了,但还能够正常运用
发现代码段中有空判别和反常抛出,那为什么在主线程这么实例化Handler没有问题呢,来接着看看 myLooper 办法
public Handler(@Nullable Callback callback, boolean async) {
// ……省掉一部分
mLooper = Looper.myLooper();
if (mLooper == null) {
// 假如Looper.myLooper()为空的话,那就会抛反常
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
// 这边就都一样了
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
前面介绍过了ThreadLocal,已然主线程不会溃散,也就意味着主线程有当地帮咱们调用了prepare 办法进行了Looper的实例化
咱们能够测验new Thread.start后在里边经过上面的这种结构实例化一下Handler看看会不会崩,然后再测验自己防止它的溃散
public static @Nullable Looper myLooper() {
// 由于prepare()不允许屡次调用,所以在实例化前能够经过该办法进行查看,为空才实例化
return sThreadLocal.get();
}
有心人应该发现了,前面的代码段中有一个办法 prepareMainLooper ,便是猜测和主线程有关,所以全局查找一下,发现在 ActivityThread类中有调用
// 帮咱们处理了一下,只留了需求重点重视的部分
// main办法是一个Activity运用运转的主进口
public static void main(String[] args) {
// ……省掉一部分办法
// 1.实例化Looper,所以是源码中帮咱们调用了,为什么呢,由于它需求用呀
Looper.prepareMainLooper();
// ……省掉一部分办法
if (false) {
// 2.这儿日志标签和 一种卡顿监控有相关,能够留个心眼
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
// 3.新办法loop!!!
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
loop()是干什么的?
主要就一个查看和一个死循环调用 loopOnce
// 又是一个静态办法呢
public static void loop() {
final Looper me = myLooper();
if (me == null) {
// 所以在调用loop()前,一定需求确保prepare过了
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// ……省掉一部分代码
for (;;) {
// 又是一个死循环,这儿一般会引申一个问题,主线程死循环不会卡死吗
// ActivityThread.main终究就这一个进口了,假如这儿结束了,运用也就结束了
// 关于休眠机制,后面再讲
if (!loopOnce(me, ident, thresholdOverride)) {
return;
}
}
}
loopOnce里主要就两步,取出音讯并分发
到这,根本的handler内容就结束了
private static boolean loopOnce(final Looper me,
final long ident, final int thresholdOverride) {
// 1.取音讯
Message msg = me.mQueue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return false;
}
// ……省掉一部分代码
try {
// 2.分发音讯,这儿的target调用的分发,也便是之前最早enqueueMessage里绑定的handler
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
}
// ……省掉一部分代码
// 3.recycleUnchecked将分发完的Message进行收回复用
// sPool的最大长度为50,空音讯被开释后会被放在表头,obtain复用时会直接取用头节点
msg.recycleUnchecked();
return true;
}
进阶,看看以下问题
以下问题均来历于此博客,经一定的收拾调整,咱们也能够直接看这个博客。也有一部分问题以为您认真地看过了上面的内容,现已融汇贯通了,所以就没有写答案
27道 Handler 经典面试题,请留意查收
1. 子线程拜访UI的 溃散原因 和 解决办法?为什么主张子线程不拜访(更新)UI?
更精确地说,
“创立UI的线程才干更新UI”
,ViewRootImpl的checkThread进行了线程查看,也便是常见的子线程拜访UI引发的溃散,当然也是有控件不在主线程创立的,感兴趣的话能够自行了解。已然知道了原因,那就能够依据场景去合理防止,比如经过handler的通讯机制\协程等手段切换到主线程进行UI操作
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
Android中UI控件是线程不安全的,加锁会下降UI拜访的效率,造成卡顿,一起也会使UI更新整体复杂化,所以归纳考虑,有了
单线程模型
机制
2. MessageQueue是干嘛呢?用的什么数据结构来存储数据?延迟音讯是怎样完成的?MessageQueue的音讯怎样被取出来的?MessageQueue没有音讯时分会怎样?堵塞之后怎样唤醒呢?
不供给答案,enqueueMessage(同步音讯、异步音讯入队)时进行唤醒
nativeWake
,当next()行列中没有音讯或下一个音讯还未到时刻履行时休眠nativePollOnce
3. 说说pipe/epoll机制?
epoll机制
是一种IO多路复用的机制,详细逻辑便是一个进程能够监督多个描绘符,当某个描绘符就绪(一般是读就绪或许写就绪),能够告诉程序进行相应的读写操作,这个读写操作是堵塞的。在Android中,会创立一个Linux管道(Pipe)
来处理堵塞和唤醒。 浅谈Android之Linux pipe/epoll 尽管看完也还有点懵hhh,就先这样吧
4. 同步屏障和异步音讯是怎样完成的?同步屏障和异步音讯有详细的运用场景吗?
差异于异步音讯的是同步音讯,也便是一般音讯,而异步音讯经过Message目标调用setAsynchronous。假如handler设置为异步,一切音讯都会设置setAsynchronous。正常情况下,异步音讯和一般音讯一样,会依据when时刻戳排队按序分发。当遇到
同步屏障
时,会唤醒线程,而且跨过期间的同步音讯
同步屏障是一种机制,特点是target为空,能够经过MessageQueue的postSyncBarrier(需求反射才干调用)办法(成功会有一个回来值code,在remove时需求用到。一起它不会真正意义上入队不走enqueueMessage,而是被直接放在了行列的恰当方位,所以它不会引发线程唤醒)发送。同步屏障不归于一种音讯,归于一种堵塞机制,意味着要
优先处理异步音讯
ViewRootImpl.scheduleTraversals
办法中,运用同步屏障和异步音讯的组合运用,用于垂直同步信号脉冲的监听,而且ASYNC信号到来之后,保证UI制作优先履行(防止期间有太多音讯,导致制作延后而导致卡顿),再移除同步屏障
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
// 发送同步屏障
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
// 这儿能够跟进去看一下,发送了一个异步音讯
// 这儿设置了一个callback:mTraversalRunnable
// 里边调用了doTraversal移除了同步屏障,并开端performTraversals
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
// postCallback -> postCallbackDelayed -> postCallbackDelayedInternal
private void postCallbackDelayedInternal(int callbackType, Object action, Object token, long delayMillis) {
// ……省掉一部分代码,这个音讯终究在Choreographer中处理
// FrameDisplayEventReceiver
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
// 设置异步音讯
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
当然有优点,也有代价。对于VSYNC信号的机制,同步音讯最多可能被延迟一帧,一起会造成同步音讯的堆积,集中处理时会有压力。所以同步音讯的履行时刻是不一定精确的!!!
那运用异步音讯需求留意点什么呢:
1.轻量级操作,否则无论怎样都会使得主线程压力变大
2.不需求和制作次序同步的,那就能够运用,否则仍是运用同步handler
Handler 音讯行列中的同步屏障——Message
5. Message是怎样找到它所属的Handler然后进行分发的?Message音讯被分发之后会怎样处理?音讯怎样复用的?
不供给答案
6. Looper是干嘛呢?怎样获取当时线程的Looper?能够屡次创立Looper吗?为什么主线程不需求独自创立Looper?(ActivityThread中做了哪些关于Handler的工作)
不供给答案
7. ThreadLocal运转机制?这种机制规划的优点?还有哪些当地运用到了ThreadLocal机制?为什么不直接用Map存储线程和目标呢?
一个Thread持有一个ThreadLocalMap,其间用Entry数组保护key(ThreadLocal目标)-value(存的泛型),类似于hashMap,会经过ThreadLocal.threadLocalHashCode和位运算得到index
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
private Entry[] table;
}
这种机制保证了线程间的数据阻隔,而用Map存储就会造成线程间共享变量的混乱污染。所以适用于线程间不共享变量的线程目标保护。需求运用时需求留意内存走漏,及时进行remove,由于key是弱引证
Choreographer中也运用了static ThreadLocal完成了主线程单例,由于key是以ThreadLocal目标的,所以static能够使得一个线程内一切操作共享
8. Looper中的quitAllowed字段是啥?有什么用?
望文生义,是否允许退出的标记(Looper.prepare->结构中传入),主线程在创立时传入的是false(即不允许退出),终究给到MessageQueue,会在调用其quit时起作用
// 这儿还有个safe标记
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
// 新的音讯就不会再进来了
mQuitting = true;
if (safe) {
// 安全退出,now时钟之前的音讯需求处理,但之后的音讯都recycler收回了
// 这也是为什么在MessageQueue.next()中,对mQuitting放这么后面
removeAllFutureMessagesLocked();
} else {
// 不安全,则直接悉数清空收回
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
那什么时分才会调用quit()?
APP需求退出,则主线程调用
子线程处理结束,则调用开释资源
9. Looper.loop办法是死循环,为什么不会卡死(ANR)?
当没有音讯的时分,会堵塞在loop的
queue.next()
中的nativePollOnce()
办法里,此时主线程会开释CPU资源进入休眠状况,直到下个音讯到达或许有业务产生。所以死循环也不会特别消耗CPU资源。而且主线程便是需求一向履行,处理各种音讯(各种Message,比如Activity的生命周期等)。而导致ANR的是音讯的处理耗时
10. Handler 的 post(Runnable) 与 sendMessage 有什么差异?Handler.Callback.handleMessage 和 Handler.handleMessage 有什么不一样?为什么这么规划?
不供给答案
11. Handler、Looper、MessageQueue、线程是一一对应联络吗?
不供给答案
12. IdleHandler是啥?有什么运用场景?
回忆一下next中,当MessageQueue中没有音讯,在堵塞之前,还会去处理IdleHandler和mBlocked堵塞标志位。能够经过MessageQueue的
addIdleHandler
进行增加
Looper.myLooper()?.queue?.addIdleHandler {
// do something
// 回来true代表keep,false即处理完就remove
false
}
性质:在不影响其他任务,在MessageQueue空闲状况下履行
运用:Android Framework层的GC场景就运用了这个机制,只有当cpu空闲的时分才会去GC
final class GcIdler implements MessageQueue.IdleHandler {
@Override
public final boolean queueIdle() {
doGcIfNeeded();
purgePendingResources();
return false;
}
}
切记~!如像
View.onDraw
中无限制调用invalidate
,不断增加同步屏障,在等到异步音讯之前会一向堵塞在next()
中,而这儿的下一个异步任务又是制作相关的,履行onDraw
而无限循环,从而无法履行IdleHandler
13. HandlerThread是啥?有什么运用场景?
HandlerThread便是为了便于在子线程中运用Handler而封装调用了Looper.parpare()&loop()的模板Thread,其间的notify\wait挺有趣的,能够看一下
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
// 与getLooper中的同步锁对应,进行唤醒
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
/**
* This method returns the Looper associated with this thread. If this thread not been started
* or for any reason isAlive() returns false, this method will return null. If this thread
* has been started, this method will block until the looper has been initialized.
* @return The looper.
*/
public Looper getLooper() {
if (!isAlive()) {
// 这时说明Thread还没start,当然也没创立Looper
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
// 这儿结合同步锁,等候Looper创立结束
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
14. IntentService是啥?有什么运用场景?
是一个内部保护了HandlerThread的Service,Service发动后,会发送Message回调
onHandleIntent()
,履行结束后经过stopSelf
进行关闭Service,一起onDestory
时也及时对Looper进行quit
,进行了合理收回。主张结合16题的内存走漏来看,这是防止该问题的杰出实践
public abstract class IntentService extends Service {
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
}
@Override
public void onCreate() {
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
public void onDestroy() {
mServiceLooper.quit();
}
15. BlockCanary运用过吗?说说原理
BlockCanary
是一个用来检测运用卡顿耗时的三方库。其原理主要对Looper设置了MessageLogging,完成对主线程音讯耗时的监听
public static void loop() {
for (;;) {
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
}
}
// 注入自己的Printer
Looper.getMainLooper().setMessageLogging(new Printer() {
private static final String START = ">>>>> Dispatching";
private static final String END = "<<<<< Finished";
@Override
public void println(String x) {
if (x.startsWith(START)) {
// 对指定格局音讯进行监听,而且依据自己的卡顿检测需求dump堆栈
startMonitor();
}
if (x.startsWith(END)) {
removeMonitor();
}
}
});
16. 说说Hanlder内存走漏问题。
“内部类持有了外部类引证”,先整理一下Handler的调用链,即ThreadLocal本身作为弱引证,即将开释时,其value:Looper直接持有Activity,因其可达而无法收回Activity,但key:ThreadLocal现已被收回了,后续value一向没有被主动收回,这本身也是ThreadLocal的内存走漏问题
Thread -> ThreadLocal -> Looper -> MessageQueue -> Message(target持有) -> Handler -> 持有或操作Activity
即当Activity退出时,Handler仍可达(比如在处理耗时操作,或MessageQueue还在排队中持有Handler)
那应该怎样处理呢?两步走
1.Handler对外部类,如Activity进行弱引证 2.Activity.onDestory时,堵截Message与Handler之间的联络,即强迫履行Message的
recycle()
,target置为null
openclassWeakReferenceHandler<T>(looper:Looper?,referencedObject:T):Handler(looper!!){
privatevalmReference:WeakReference<T>=WeakReference(referencedObject)
protectedvalreferencedObject:T?
protectedget()=mReference.get()
}
// Activity 毁掉的时分,假如子线程任务尚未结束,及时中止 Thread:
overridefunonDestroy(){
...
// 协程的话,会相关lifecycleScope进行生命周期监听并及时cancel
thread.interrupt()
}
//假如子线程中创立了 Looper 并成为了 Looper 线程的话,须手动 quit。比如HandlerThread:
overridefunonDestroy(){
...
// 假如用协程进行线程切换的话,也不存在这个问题,由于直接被cancel掉了
handlerThread.quitSafely()
}
//主线程的 Looper 无法手动 quit(由于设置了quitAllow=false),所以还需手动清空主线程中 Handler 未处理的 Message:
overridefunonDestroy(){
...
mainHandler.removeCallbacksAndMessages(null)
}
※1:Message 在履行`recycle()`后会铲除其与和 Main Handler 的引证联络
※2:Looper 子线程调用 quit 时会清空 Message,所以无需针对子线程的 Handler 再作 Message 的清空处理了
有任何 主张\纠正 欢迎
下方谈论
或联络作者