Handler相关知识点

Handler相关知识点

七月 20, 2020

Handler常见知识总结

version 0.1

[TOC]

主要组件:

  1. Looper: 一个线程可以产生一个Looper对象,由它来管理此线程里的MessageQueue(消息队列)。
  2. Handler: 你可以构造Handler对象来与Looper沟通,以便push新消息到MessageQueue里;或者接收Looper从Message Queue取出)所送来的消息
  3. Message Queue(消息队列):用来存放线程放入的消息,采用单链表实现。
  4. 线程:UIthread 通常就是main thread,而Android启动程序时会替它建立一个MessageQueue。

过程简单概述

郭霖-Android异步消息处理机制完全解析,带你从源码的角度彻底理解

Android UI线程是不安全,同时也不能进行耗时操作,安卓提供了Handler方便开发者来进行线程间切换与通信。
Handler在创建时需要传递一个线程的Looper,Looper内存在对应线程的消息队列(MessageQueue)(通过ThreadLocal获取)。Handler将消息(Message)发送给Looper,由Looper放入消息队列。而Looper又通过循环从消息队列中取出消息再交给Handler处理。

-c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class LooperThread extends Thread {
public Handler mHandler;

public void run() {
Looper.prepare();

mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};

Looper.loop();
}
}

具体分析

初始化过程

Handler构造方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper(); //这里从Looper中获取mLooper对象,如果为空则抛出异常
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;//从Looper中获取消息队列
mCallback = callback;//callback赋值
mAsynchronous = async;//是否是同步处理消息
}

Looper.myLooper():

1
2
3
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}

可以看到初始化的过程中就是要从Looper中获取myLooper,那与我们直接使用之间的区别是是否有调用Looper.prepare()方法。

Looper.prepare()

1
2
3
4
5
6
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}

首先判断sThreadLocal中是否已经存在Looper,没有的话则创建一个新的进去,而且必须满足只能有一个Looper对象。

(主线程不需要,因为系统在ActivityThread启动时已经调用了该方法)


发送消息过程

最终调用为public boolean sendMessageAtTime(Message msg, long uptimeMillis)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
boolean sent = false;
MessageQueue queue = mQueue;
if (queue != null) {
msg.target = this;
sent = queue.enqueueMessage(msg, uptimeMillis);
}
else {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
}
return sent;
}

其中msg为发送的 Message 对象,uptimeMillis表示 发送消息的时间。如果没有延迟,则延迟时间为0。注意 它的时间是系统 开机到当前时间的毫秒数再加上延迟时间

1
2
3
4
5
6
7
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

入队过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
final boolean enqueueMessage(Message msg, long when) {
if (msg.when != 0) {
throw new AndroidRuntimeException(msg + " This message is already in use.");
}
if (msg.target == null && !mQuitAllowed) {
throw new RuntimeException("Main thread not allowed to quit");
}
synchronized (this) {
if (mQuiting) {
RuntimeException e = new RuntimeException(msg.target + " sending message to a Handler on a dead thread");
Log.w("MessageQueue", e.getMessage(), e);
return false;
} else if (msg.target == null) {
mQuiting = true;
}
msg.when = when;
Message p = mMessages;
//入队过程
if (p == null || when == 0 || when < p.when) {//先判断是否需要立即发送或延迟时间小于队列中第一个消息的延迟时间的话则放入队首
msg.next = p;
mMessages = msg;
this.notify();
} else {
Message prev = null;
while (p != null && p.when <= when) {//否则找到合适位置入队
prev = p;
p = p.next;
}
msg.next = prev.next;
prev.next = msg;
this.notify();
}
}
return true;
}

入队过程中只有mMessages来表示当前待处理消息,同时按照时间来升序排列。同时注意虽然称之为消息队列但是我们看见其实这是一个单链表的结构,优点是增删的性能较好。

出队过程

出队主要是通过Looper.loop()方法,所以使用中只是调用prepare入队是不能让handler工作起来的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
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();

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);
}
//这里有一个执行耗时阈值的检测,统计执行时间差如果超过阈值会进行打印,并获取堆栈信息
final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;

final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
final long end;
try {
//开始调用target的dispatchMessage来分发消息
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (slowDispatchThresholdMs > 0) {
final long time = end - start;
if (time > slowDispatchThresholdMs) {
Slog.w(TAG, "Dispatch took " + time + "ms on "
+ Thread.currentThread().getName() + ", h=" +
msg.target + " cb=" + msg.callback + " msg=" + msg.what);
}
}

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();
}
}

可以看见主要是进入了死循环,并不断的调用消息队列的next()来获取消息,出队操作中如果没有消息或消息发送时间还未到会进入阻塞,只有手动让Looper停止才可以使next()方法返回null,从而退出循环

具体出队过程分析: //TODO

分发事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
//如果消息msg.callback不为空则回调给callback,赋值的地方稍后分析
handleCallback(msg);
} else {
//如果初始化时候有callback,则会在本身的callback中回调handleMessage
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//通常情况会直接回调给handleMessage去处理
handleMessage(msg);
}
}

而其中的handleCallback 实际上是调用了run()方法,
handleMessage 是回调去处理,mCallback赋值地方下面分析。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private static void handleCallback(Message message) {
message.callback.run();
}

/**
* Callback interface you can use when instantiating a Handler to avoid
* having to implement your own subclass of Handler.
*
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
public interface Callback {
public boolean handleMessage(Message msg);
}

/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}

总结:

由于Handler总是依附于创建时所在的线程(Looper决定),比如在主线程中创建,而子线程无法直接对UI进行操作,于是可以通过调用Handler将消息经过一些列的发送,入队,出队最后在主线程handleMessage回调

  • Handler.post() 方法:

这里调用了sendMessageDelayed(), 并将时间设置为 0ms,mCallback也是这个时候去赋值的。

1
2
3
4
5
6
7
8
9
10
11
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
private final Message getPostMessage(Runnable r) {
//obtain是从消息缓存池中获取避免多次new的优化
Message m = Message.obtain();
//这里将runnable对象赋值给了callback
m.callback = r;
return m;
}
  • View.post() 方法:

最终还是调用了handler.post方法

1
2
3
4
5
6
7
8
9
10
11
public boolean post(Runnable action) {
Handler handler;
if (mAttachInfo != null) {
handler = mAttachInfo.mHandler;
} else {
//如果还没有被依附则会放入队列等待直到被依附后调用
ViewRoot.getRunQueue().post(action);
return true;
}
return handler.post(action);
}
  • runOnUiThread() 方法:
    也是调用了post,同时如果是主线程则直接run
1
2
3
4
5
6
7
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}

常见问题与优化

Android中为什么非UI线程不能更新UI?

UI线程工作机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

public static void main(String[] args) {
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);

if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}

if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}

// End of event ActivityThreadMain.
//主线程会一直死循环运行 也就是阻塞
Looper.loop();
//如果死循环退出了,程序就会崩掉抛出异常
throw new RuntimeException("Main thread loop unexpectedly exited");
}

UI线程就是zygote直接fork出来的进程中的线程,就是Activty中的main


为什么UI线程设计成不安全的?

如果在子线程子线程中去更新ui,必定要对view更新的操作加锁,加锁的效率很受影响,不能接受

  • ui具有可变性,甚至是高频可变性
  • ui对响应时间非常敏感
  • ui组件必须批量绘制,保证效率

非UI线程一定不能更新UI吗?

可以间接更新,比如postpostinvalidate。还有SurfaceView是直接在子线程中去更新UI的,在Activity的onCreate生命周期中也可以在子线程中更新ui。

-c

安卓更新UI的时候会去做线程检测,检测创建这个UI的线程是不是要更新ui的线程,更新UI就是在ViewRootImpl中做requestLayout,做线程检测。

但是ViewRoot是在onResumeaddDecView去实现的,如果在onCreate中创建子线程更新UI 也许这时候还没有创建Viewroot,这时候就绕开了检测

在子线程弹Toast是可以的,因为Toast是在windowManager上弹的,与Activity无关,也就与ViewRoot无关,但是由于Toast里面用到了handler,所以在子线程中***Looper.prepare()***一下。所以,在子线程中是可以更新ui的

Handler常见的内存泄露问题

掘金 - 你的 Handler 内存泄露 了吗?

Handler postDelay方法是否可靠?

结论:不可靠,但需要深入分析

  • 大于Handler Looper的周期时基本可靠(例如主线程>50ms)
  • Looper 负载越高,任务越容易积压,进而导致卡顿
  • 不要用Handler的delay做计时使用

-c

当事件生产过多,在队列中事件产生堆积时要注意。

这里还是要分析入队的过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
boolean enqueueMessage(Message msg, long when) {
...
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;
needWake = mBlocked;
} else {
//根据when时间去遍历然后放入队列
...
}
//进入阻塞态
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}

loop()分析

Looper.loop过程 中看到了调用了Message的next方法,这里是我们要分析的重点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
 Message next() {
...
for (;;) {
...
//如果这里的值是-1则会在底层阻塞
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) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
//设置时间要休眠的时间,让nativePollOnce一段时间后醒过来
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
...
return msg;
}
}
...
}
}

这里,消息等待阻塞机制主要是在nativePollOnce中去实现,当前消息时间还没有到,或者没有消息了,这时候会进行等待休眠状态不会消耗cpu,这是采用了Linux的IO多路复用机制

-c

大致过程描述为从MessageQueue调用next如果没有消息会进入底层的pollOnce然后去调用epoll_wait这个文件描述符上去等待消息。
当有新消息进来需要处理时,会让底层的调用wake然后通过write后去让epoll_wait唤醒,再通知上层继续。

总结:
言而言之Looper采用了epoll机制,当没有消息来得时候会处于阻塞状态wait,从而不消耗CPU,当消息需要通知的时候会通过wake来通知上层继续工作。


为什么Handler不会阻塞主线程造成ANR?(Looper死循环为什么不会导致CPU占用率很高?)

ANR

首先明确,造成ANR的主要是事件没有在规定时间内分发。只有当应用程序的UI线程响应超时才会引起ANR,超时产生原因一般有两种。

  • 当前的事件没有机会得到处理,例如UI线程正在响应另一个事件,当前事件由于某种原因被阻塞了。
  • 当前的事件正在处理,但是由于耗时太长没能及时完成。
ANR 类型
  • ServiceTimeOut:
    1. 前台服务20s
    2. 后台服务200s
  • BroadcaseQueue Timeout:
    1. 前台广播 10S
    2. 后台广播 60S
  • ContentProvider Timeout:10S
  • InputDispatching Timeout: 5S
分析一次ANR过程

无论是四大组件或者进程等只要发生ANR,最终都会调用***AMS.appNotResponding()***方法

以Service TimeOut为例:

ActivityServices类中:

####### 埋入炸弹

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

private final void realStartServiceLocked(ServiceRecord r, ProcessRecord app, boolean execInFg) throws RemoteException {
...
//发送delay消息(SERVICE_TIMEOUT_MSG) 在这里埋入炸弹
bumpServiceExecutingLocked(r, execInFg, "create");
try {
...
//最终执行服务的onCreate()方法
app.thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
app.repProcState);
} catch (DeadObjectException e) {
mAm.appDiedLocked(app);
throw e;
} finally {
...
}
}

private final void bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why) {
...
scheduleServiceTimeoutLocked(r.app);
}

void scheduleServiceTimeoutLocked(ProcessRecord proc) {
if (proc.executingServices.size() == 0 || proc.thread == null) {
return;
}
long now = SystemClock.uptimeMillis();
Message msg = mAm.mHandler.obtainMessage(
ActivityManagerService.SERVICE_TIMEOUT_MSG);
msg.obj = proc;

//当超时后仍没有remove该SERVICE_TIMEOUT_MSG消息,则执行service Timeout流程
//同时判断根据是否是前台进程来设置超时时间为20s与200s
mAm.mHandler.sendMessageAtTime(msg,
proc.execServicesFg ? (now+SERVICE_TIMEOUT) : (now+ SERVICE_BACKGROUND_TIMEOUT));
}

可以看见发送了一个delay的消息一旦达到超时时间就会触发,那提前拆除这个消息的地方就是在onCreate方法后紧接调用system_serverserviceDoneExecuting

####### 拆除炸弹

1
2
3
4
5
6
7
8
9
10
11
12
13
private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying, boolean finishing) {
...
if (r.executeNesting <= 0) {
if (r.app != null) {
r.app.execServicesFg = false;
r.app.executingServices.remove(r);
if (r.app.executingServices.size() == 0) {
//当前服务所在进程中没有正在执行的service
mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
...
}
...
}

当service启动完成后,则移除service创建超时的消息否则就会触发。

####### 引爆炸弹

system_server进程中有一个Handler线程, 名叫”ActivityManager”.当倒计时结束便会向该Handler线程发送 一条信息 SERVICE_TIMEOUT_MSG

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
final class MainHandler extends Handler {
public void handleMessage(Message msg) {
switch (msg.what) {
case SERVICE_TIMEOUT_MSG: {
...
mServices.serviceTimeout((ProcessRecord)msg.obj);
}
break;
...
}
...
}
}

void serviceTimeout(ProcessRecord proc) {
...
if (anrMessage != null) {
//当存在timeout的service,则执行appNotResponding
mAm.appNotResponding(proc, null, null, false, anrMessage);
}
}

final void appNotResponding(ProcessRecord app, ActivityRecord activity, ActivityRecord parent, boolean aboveSystem, final String annotation) {
...
//Bring up the infamous(声名狼藉)App Not Responding dialog
//弹出ANR对话框
Message msg = Message.obtain();
HashMap<String, Object> map = new HashMap<String, Object>();
msg.what = SHOW_NOT_RESPONDING_MSG;
msg.obj = map;
msg.arg1 = aboveSystem ? 1 : 0;
map.put("app", app);
if (activity != null) {
map.put("activity", activity);
}

//向ui线程发送,内容为SHOW_NOT_RESPONDING_MSG的消息
mUiHandler.sendMessage(msg);
}

经过一系列的跟踪,终于看见了一直以来深恶痛觉的ANR错误的由来。其中也夹杂着各种信息收集trace等,如果展开篇幅太长,我也参考了这里:


Looper.loop()

ActivityThread的main方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static void main(String[] args) {

...

Looper.prepareMainLooper();

ActivityThread thread = new ActivityThread();
thread.attach(false);

if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}

...

Looper.loop();

throw new RuntimeException("Main thread loop unexpectedly exited");
}

-c

Looper是一个整体概念,ANR主要是执行到某一环节执行耗时的一种监控。所以有了这个区别,发现应该更侧重于Looper为何不会导致CPU占用率过高?

由于Looper消息底层是native的epoll_wait

  • Looper::pollInner:
1
2
3
4
5
6
7

mEpollFd = epoll_create(EPOLL_SIZE_HINT);
int result = epoll_ctl(mEpollFd,EPOLL_CTL_ADD,mWakeEventFd, &eventItem);
...

struct epoll_event eventItems[EPOLL_MAX_EVENTS];
int eventCount = epoll_wait(mEpollFd.eventItems,EPOLL_MAX_EVENTS,timeoutMills);

-c

IO多路复用部分会有一个监控的红黑树,开始时对epoll监听描述符做了一个配置连接的管道,如果事件就绪会移动到就绪列表,绿色的部分,然后去通知epoll_wait。这时如果Looper卡在了epoll_wait则会把它唤醒,去处理消息。


消息屏障是什么?

在Android的消息机制中,其实有三种消息: 普通消息异步消息消息屏障

消息屏障也是一种消息,但是它的target为 null。可以通过MessageQueue中的postSyncBarrier方法发送一个消息屏障(该方法为私有,需要反射调用)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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;
//按照时间顺序将消息插入到消息队列中
......
return token;
}
}

在消息循环中,如果第一条消息就是屏障消息,就往后遍历,看看有没有异步消息: 如果没有,则无限休眠,等待被唤醒 如果,就看离这个消息被触发时间还有多久,设置一个超时时间,继续休眠

异步消息和普通消息一样,只不过它被设置setAsynchronoustrue。有了这个标志位,消息机制会对它有些特别的处理,使异步消息具有优先处理的权利

这时候我们回顾将消息添加到消息队列中时,可以发现, 其实并不是每一次添加消息时,都会唤醒线程 。 当该消息插入到队列头时,会唤醒该线程; 当该消息 没有插入到队列头 ,但队列 头是屏障 ,且该消息是队列中 靠前的一个异步消息,则会唤醒线程,执行该消息。

调用MessageQueue.removeSyncBarrier 方法可以移除指定的消息屏障。

掘金 - Android Handler那些事儿

Handler 队列优化

互斥消息取消

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
enum Category{
RENDER,START,STOP
}

enum MessageType {
public void stop() {
Message message = Message.obtain(handler, STOP_MAP.what);
message.obj = STOP_MAP.category;
for (Category category : Category.values()) {
if(category.ordinal() <= STOP_MAP.category.ordinal()){
//当满足条件时清除其他消息,有优先级
handler.removeCallbacksAndMessages(category);
}else{
break;
}
handler.sendMessage(message);
}
}
}

复用消息

调用Message 的**obtain()**方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
public static 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;
}
}
return new Message();
}

IdleHandler

1
2
3
4
5
6
7
8
private MessageQueue.IdleHandler idleHandler = new MessageQueue.IdleHandler() {
@Override
public boolean queueIdle() {
//处理事情
return false;//false 代表只在空闲时执行一次,执行完移除,true会多次触发
}
};

调用方式:

1
2
Looper.myQueue().addIdleHandler(idleHandler);

Glide 3.X中用此方法来移除gc调用的图片弱引用,4.X中移除。

独享Looper

1
2
3
private HandlerThread handlerThread = new HandlerThread("Token-Thread")
handlerThread.start();
private Handler tokenHandler = new Handler(handlerThread.getLooper());

更新日志:

版本 时间 说明
version 0.1 2020年05月22日17:50:30 初版整理常用复习知识点