Handler,我要你有何用?

Android UI 框架是单线程机制,这也就意味着如果要保持界面的高刷新率,对于任何的耗时任务而言,对于它来说最好的选择就是跑在别的线程里面.为了与UI线程主线程进行区分,一般这些线程被称作工作线程或是后台线程.那么在一个工作流程中,如何做到从一个线程跑到另一个线程,而又从另一个线程跑回到最开始的线程呢.

这时候就要祭出 Android 系统中的 Handler, 位于天堑之间,人们只能通过索道来往通行.而对于两个线程而言呢,其实也就是把人挂在索道上,然后把一个个传过去.从而引申出了另外两个概念,消息和消息队列.那么Handler是谁,就是负责把人送过去的人.

搬运工主要的职责有两个:安排什么时候送过去,当然送到对岸之后,跟现在的地方也就没什么关系.

Hanlder->MessageQueue->Message

Handler 操作的对象是 MessageQueue,而 MessageQueue 的操作对象则是 Message.

但是,这里我们需要注意的是,说是队列,但是我们没有使用任何Java集合的类,Message是以内部引用方式存在的单向链表队列.

我们先忽略掉细节先从,handler的构造函数开始:

映入眼脸吓一跳:

Handler()、Hanlder(Callback)、Handler(Looper)、Handler(Looper,Callback)
、Handler(boolean)、Hanlder(Callback,boolean)、Hanlder(Looper,Callback,boolean)

其实就是啥都是默认的,传个callback、Looper、boolean 的搭配组合.

我们需要看的是最常用的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public Handler(@Nullable 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();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}

首先,如果Handler不是静态的,说你肯能造成内存泄漏,当然现在不是重点,我们先过.

1
mLooper = Looper.myLooper();

相当于说,我去looper这个map里面拿,每个线程独一份的Looper,当然如果没有初始化的话,这个looper肯定是空的.

如果我在线程中,在没有初始化Looper之前去初始化Handler,报错事必然的,

1
E/Handler: This is not main thread, and the caller should invoke Looper.prepare()  and Looper.loop()called byandroid.os.Handler

大致意思是,应该先调用Loope.prepare(),并且在Handler中你要去调用 Looper.loop().

也就是说:

1
2
3
4
thread {
Looper.prepare()
val handler = Handler()
}

那在Looper.prepare()这步到底做了什么呢?

1
2
3
4
5
6
7
8
9
10
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));
}

初始化当前线程的Looper,并且如果已经有Looper的情况下去调用,肯定是会报错的.

譬如:

1
2
3
4
5
6
thread {
Looper.prepare()
Looper.prepare()
val handler = Handler()

}
1
2
3
4
5
6
7
8
2020-07-08 14:06:44.322 29171-29332/cn.jinyulei.handlertest E/AndroidRuntime: FATAL EXCEPTION: Thread-2
Process: cn.jinyulei.handlertest, PID: 29171
java.lang.RuntimeException: Only one Looper may be created per thread
at android.os.Looper.prepare(Looper.java:110)
at android.os.Looper.prepare(Looper.java:105)
at cn.jinyulei.handlertest.MainActivity$onCreate$2.invoke(MainActivity.kt:27)
at cn.jinyulei.handlertest.MainActivity$onCreate$2.invoke(MainActivity.kt:13)
at kotlin.concurrent.ThreadsKt$thread$thread$1.run(Thread.kt:30)

同时,我们看到 Handler 中的 mQueue不过指向从 looper中mQueue的. 而Looper.loop()中也是干了些什么呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
...
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
....

msg.recycleUnchecked();


}

无限循环,从消息队列里面拿消息,拿到之后就进行分发,分发之后进行回收.

而这里面的target,就是我们的Handler.

很明显以上做的事一个出队列的操作.但是至于怎么消息是怎么进来的呢?

我们有两个系列的方法分别来把不同类型的消息塞进来,post系列以及sendMessage系列

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
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}


public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) {
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}


public final boolean postAtTime(
@NonNull Runnable r, @Nullable Object token, long uptimeMillis) {
return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}

public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {
return sendMessageDelayed(getPostMessage(r), delayMillis);
}

public final boolean postDelayed(
@NonNull Runnable r, @Nullable Object token, long delayMillis) {
return sendMessageDelayed(getPostMessage(r, token), delayMillis);
}
public final boolean postAtFrontOfQueue(@NonNull Runnable r) {
return sendMessageAtFrontOfQueue(getPostMessage(r));
}

按照求同存异的法则,通过比较这些方法可以知道,传入的都是要干的事情,然后通过getPostMessage(r)把干的事情存到消息里面,然后通过发送消息的一系列方法,把消息压入队列.

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
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}

public final boolean sendEmptyMessage(int what)
{
return sendEmptyMessageDelayed(what, 0);
}

public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}

public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageAtTime(msg, uptimeMillis);
}

public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

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

public final boolean sendMessageAtFrontOfQueue(@NonNull Message msg) {
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, 0);
}

别看那么多东西最终调用的都是这么一个方法:

1
2
3
4
5
6
7
8
9
10
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();

if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}

参考