Android 消息机制

本文目的:

    • 详细梳理一下 Android 的消息机制.其实说白了就是 Handler 的使用和原理剖析

我们都知道 Handler.并且日常使用也是非常的频繁.

那么随之而来的几个问题

#1.Handler 到底是什么呢?

    • 这个问题,呃…还是放到最后来总结一下吧.总不能说 Handler 就是一个类而已(就跟问你 Android到底是什么呢?)

#2.Handler 是用来干什么的?

    • 本质就是将多线程转换成单线程.

      举个例子:
  • 在 Android 我们常常在子线程做一些操作之后(如:下载图片),要回到主线程去 ui,通常使用的就有 Handler.因为如果每个线程都可以更新 ui 的情况下,界面就变的不可控了.如果加锁的话,那性能就成一个问题了 . so Handler 的作用就是将多线程变成单线程,从而使界面 ui 按照我们预期的效果更新.

#3.Handler 的使用(对此熟悉的可直接跳过)

1)send…

send 的方式

看一个简单的例子吧

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
public class MainActivity extends AppCompatActivity {

private final int type = 1;
private TextView mTextView;

private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if(type == msg.what)
mTextView.setText("finish");
}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

mTextView = findViewById(R.id.textView);

new Thread(new Runnable() {
@Override
public void run() {
handler.sendEmptyMessage(type);
}
}).start();
}
}

这是一个非常典型的通过 send 的方式来使用 Handler 的情况.

从代码中可以看到:

  • 在主线程中创建了 Handler 并重写了其 handleMessage 方法
  • 在子线程中就可以通过 handler 的 send…方法来发送消息
  • 最终的消息会在 handleMessage 中被处理

2)post 方式

还是看代码说话

1
2
3
4
5
6
7
8
9
10
11
new Thread(new Runnable() {
@Override
public void run() {
handler.post(new Runnable() {
@Override
public void run() {
Log.e("Cml",Thread.currentThread().getName());//main
}
});
}
}).start();

post的方式也可以实现我们想要的结果即:切换到主线程工作

其实 post 的本质上也是 send 的方式.我们跟进去源码看一看

1
2
3
4
//Handler.java
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r), 0);
}

3)obtainMessage方式

1
2
3
4
5
6
new Thread(new Runnable() {
@Override
public void run() {
handler.obtainMessage(1).sendToTarget();
}
}).start();

同样的 obtain…的本质也是 send 方式.我们再看一看源码

1
2
3
public final Message obtainMessage(int what){
return Message.obtain(this, what);
}

可以看到这里获取了一个 Message 对象

1
2
3
4
//此处的 target 是一个 Handler 类型的对象
public void sendToTarget() {
target.sendMessage(this);
}

可以看到最后走的还是 send…系列的方法

#4.Handler 原理剖析

Handler & Looper & MessageQueue

说到 Handler 的实现机制,其实就是这几个类之间的相互协作

  • Handler: 负责发送消息到 MessageQueue 和最后的处理消息
  • Looper : 是一个循环,不断的从 MessageQueue 中取出消息后回调 Handler 的 dispathMessage()
  • MessageQueue: 一个Message队列,通过Message 的 next 属性,也就是一个单项链表来实现
  • Message: 将需要传递的各种参数封装成 Message.

我们看一个图来理解一下

Handler

接下来我们一步一步跟着源码走一下,彻底弄懂 Handler 的机制.

从入口send…开始(因为 post 方式和 obtain 方式最后都是通过 send 来发送消息的)

1
handler.sendEmptyMessage(type);

跟着这个方法往下走,发现所有的 send…方法最终都是走到了这个方法里面

再看看 enqueueMessage()这个方法

可以看到通过 queue 的enqueueMessage()将 msg 传了进去

MessageQueue

我们进 MessageQueue 看看

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
//MessageQueue

boolean enqueueMessage(Message msg, long when) {
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) {
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;
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;
}

这个代码稍微有点多,不过我们只看关键部位即可.

msg.next = p;

看这里看到队列是通过 next 来指向了下一个 message,利用单向的链表来实现的 queue

Looper

接下来我们接着看看 Looper 是怎么取消息的

首先看看 Looper 的 prepare() ,其实就是实例化了一个 Looper 对象.

另外 Looper 还提供了一个prepareMainLooper()来供主线程调用.

可以看到被创建后存储在了 ThreadLocal 中,如果再次创建就会报错.

至于 ThreadLocal 这里不展开介绍了.

那是不是这样呢,我们一起来看看

在 main()方法中的第6478行我们看到了.Looper.prepareMainLooper()来实例化 Looper.

这也就是我们在主线程使用 Handler 为什么不用自己prepare Looper了,而在子线程就需要.因为系统默认在主线程帮我们做了这一步操作.

记得 第6494行的Looper.loop() 让 looper 循环起来啊

这样的错误

1
2
java.lang.RuntimeException: 
Can't create handler inside thread that has not called Looper.prepare()

我们是不是就知道是为什么了啊.当然解决也就不是问题了.

接下来继续看看 loop()做了什么

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

这个方法代码有点多,不过不用怕.

我们可以看到有一个 for(,,)这样的死循环,而且在 for()里面首先看到的就是这样

1
2
3
4
5
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}

可以看到,如果没有 message 的时候就会阻塞在这里.

那是怎么处理消息呢,里面有这样一句代码

1
msg.target.dispatchMessage(msg);

而msg.target 我们在前面见过,就是 handler

Handler 又回来啦

so,取到消息之后把消息放到了 handler 中去处理

我们跟着进入瞧一瞧

这里比较简单了,就是一个回调的处理.可以看到首先判断 callback 是否为空,为空的时候就会走到我们重写的handleMessage()方法中.

你问我 callback 是什么?

看看 Handler 的构造器就知道了,在实例化 Handler的时候是可以传一个 callback 进去的.

至此,我们整个源码分析就一圈完事儿了

如下的问题是不是就都可以解决了

1.为什么在子线程使用 handler 不初始化 looper 就会报错

因为主线程默认帮我们初始化了looper了,so 就不用我们自己初始化了.

2.handler是怎么实现线程切换的

从上面的源码中我们可以知道 Handler 是在主线程被实例化的,而最后的 message 也被 looper 通过 dispathMassage()送到了 Handler 中来处理,so 就实现了线程的切换