Handler机制基本原理

定义

由于Android本身是线程不安全的,为了解决多线程并发的问题,Android提供了一套原生异步消息处理机制——Handler机制。Handler机制主要涉及到四个类:Handler、Looper、Message和MessageQueue。

Handler机制

使用方法

创建Handler对象

方式一:在主线程中创建Handler对象,并重写其handMessage方法。

1
2
3
4
5
6
7
8
9
10
11
12
private static Handler uiHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case 1:
// 处理消息
break;
default:
break;
}
}
};

由于非静态内部类、匿名内部类会隐式地持有外部类的引用,虽然仅仅持有引用并不会引起内存泄漏,但是如果有什么延时的操作,而且进行某个延时操作的对象还必须以持有外部类为基础才能进行的,这个时候就内存泄漏了 。所以若使用方式一,则需要在Activity的onDestroy()方法中调用Handler中的**removeCallbackAndMessages(null)**方法防止内存泄漏。

方式二(推荐):使用静态内部类+弱引用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private final UIHandler uiHandler = new UIHandler(this);

private static class UIHandler extends Handler{

private final WeakReference<MainActivity> mActivity;

public UIHandler(MainActivity activity){
mActivity = new WeakReference<MainActivity>(activity);
}

@Override
public void handleMessage(Message msg) {
if (mActivity.get()!=null){
switch (msg.what){
case 1:
break;
default:
break;
}
}
}
}

在子线程中使用下列方法将Message发出

  1. post(Runnable)
  2. postDelayed(Runnable,long)
  3. sendMessage(Message)
  4. sendMessageDelayed(Message, long)

核心类解析

消息的封装类:Message

Message的主要功能是进行消息的封装,同时可以指定消息的操作形式,常用的属性有:

  1. public int what:最常用变量,作为一个Message的标识,
    定义此Message属于何种操作

  2. public Object obj:变量,用于定义此Message传递的信息数据,通过它传递信息

  3. public int arg1:变量,传递一些整型数据时使用

  4. public int arg2:变量,传递一些整型数据时使用

  5. Handler target:变量,发送和处理该Message的Handler

    在整个消息处理机制中,Message又叫Task,封装了任务携带的信息和处理该任务的Handler。Message的用法比较简单,但是有这么几点需要注意:

(1)尽管Message有public的默认构造方法,但是你应该通过Message.**obtain()**来从消息池中获得空消息对象,以节省资源。

(2)如果你的Message只需要携带简单的int信息,请优先使用Message.arg1和Message.arg2来传递信息,这比用Bundle更省内存

(3)擅用Message.what标识信息,以便用不同方式处理Message。

(4)使用setData()存放Bundle对象。

消息循环器:Looper

在使用Handler处理Message时,需要Looper来完成。在一个Activity中,系统会自动帮用户启动Looper对象,而在一个用户自定义的类中,则需要用户手动调用Looper类中的方法,然后才可以正常启动Looper对象。使用Looper类创建Looper线程很简单:

1
2
3
4
5
6
7
8
9
10
11
12
public class LooperThread extends Thread {
@Override
public void run() {
// 将当前线程初始化为Looper线程
Looper.prepare();

// ...其他处理,如实例化handler

// 开始循环处理消息队列
Looper.loop();
}
}

Looper中常用的方法:

prepare(boolean quitAllowed)

prepare()方法会创建一个Looper对象,将当前线程初始化为一个Looper线程。源码如下:

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

其中sThreadLocalThreadLocal对象,什么是ThreadLocal?

ThreadLocal,线程本地变量,是一个本地线程副本变量工具类。主要用于将私有线程和该线程存放的副本对象做一个映射,各个线程之间的变量互不干扰,在高并发场景下,可以实现无状态的调用,特别适用于各个线程依赖不通的变量值完成操作的场景。

ThreadLocal为每个线程创建了一个副本,用于访问副本变量。

可以看到每个线程中的Looper对象其实是一个ThreadLocal,即线程本地存储(TLS)对象,这样Thread跟Looper就形成了一一对应关系,也就是说,一个线程只能有一个Looper。

loop()

looper()则是开启消息循环,不断获取MessageQueue中的消息,并发送给相应的Handler对象。

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
public static void loop() {
final Looper me = myLooper(); //从ThreadLocal中get到当前looper对象
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;//获取与当前looper绑定的MessageQueue

// 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(); // 当没有消息时会阻塞
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 traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
//将处理工作交给Message的target,即handler
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}

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

myLooper()

myLooper()方法的作用是从当前线程的ThreadLocal中获取当前Looper对象。

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

综上,Looper有以下几个要点:

1)每个线程有且只能有一个Looper对象,它是一个ThreadLocal

2)Looper内部有一个消息队列,loop()方法调用后线程开始不断从队列中取出消息执行

3)Looper使一个线程变成Looper线程。

消息的发送者和处理者:Handler

Message对象封装了所有的消息,而这些消息的操作需要android.os.Handler类完成。什么是Handler?Handler 起到了处理MessageQueue上的消息的作用(只处理由自己发出的消息),即通知MessageQueue它要执行一个任务(sendMessage),并在loop到自己的时候执行该任务(handleMessage),整个过程是异步的。Handler创建时会关联一个Looper,默认的构造方法将关联当前线程的Looper,不过这也是可以set的。默认的构造方法:

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
public class handler {
final MessageQueue mQueue; // 关联的MessageQueue
final Looper mLooper; // 关联的Looper
final Callback mCallback;
// 其他属性
public Handler() {
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());
}
}
// 默认将关联当前线程的looper
mLooper = Looper.myLooper();
// Looper不能为空,即该默认的构造方法只能在Looper线程中使用
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
//把关联looper的MessageQueue作为自己的MessageQueue,因此它的消息将发送到关联looper的MessageQueue上
mQueue = mLooper.mQueue;
mCallback = null;
}

··· ···
}

Handler发送消息

发送消息的方法有很多,但归根结底都是调用了sendMessageAtTime()方法。

在子线程中通过Handler的post()方式或send()方式发送消息,最终都是调用了sendMessageAtTime()方法。

post方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean postAtTime(Runnable r, long uptimeMillis)
{
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
{
return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}
public final boolean postDelayed(Runnable r, long delayMillis)
{
return sendMessageDelayed(getPostMessage(r), delayMillis);
}

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
public final boolean sendMessage(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(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

就连子线程中调用Activity中的runOnUiThread()中更新UI,其实也是发送消息通知主线程更新UI,最终也会调用sendMessageAtTime()方法。

1
2
3
4
5
6
7
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}

sendMessage() -> sendMessageDelayed() -> sendMessageAtTime() -> enqueueMessage()

sendMessageAtTime()方法的源码:

1
2
3
4
5
6
7
8
9
10
11
12
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
//其中mQueue是消息队列,从Looper中获取的
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
//调用enqueueMessage方法
return enqueueMessage(queue, msg, uptimeMillis);
}

可以看到sendMessageAtTime()方法的作用很简单,就是调用MessageQueue的enqueueMessage()方法,往消息队列中添加一个消息。

enqueueMessage()方法的源码为:

1
2
3
4
5
6
7
8
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
// 调用MessageQueue的enqueueMessage方法将消息入队
return queue.enqueueMessage(msg, uptimeMillis);
}

下面来看enqueueMessage()方法的具体执行逻辑。

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
boolean enqueueMessage(Message msg, long when) {
// 每一个Message必须有一个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) {
if (mQuitting) { //正在退出时,回收msg,加入到消息池
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
//p为null(代表MessageQueue没有消息) 或者msg的触发时间是队列中最早的, 则进入该该分支
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
//将消息按时间顺序插入到MessageQueue。一般地,不需要唤醒事件队列,除非
//消息队头存在barrier,并且同时Message是队列中最早的异步消息。
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;
prev.next = msg;
}
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}

MessageQueue是按照Message触发时间的先后顺序排列的,队头的消息是将要最早触发的消息。当有消息需要加入消息队列时,会从队列头开始遍历,直到找到消息应该插入的合适位置,以保证所有消息的时间顺序。

Handler分发和处理消息

在loop()方法中,获取到下一条消息后,执行msg.target.dispatchMessage(msg)来分发消息到目标Handler对象。

下面就来看dispatchMessage()方法具体的执行流程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
//当Message存在回调方法,回调msg.callback.run()方法;
handleCallback(msg);
} else {
if (mCallback != null) {
//当Handler存在Callback成员变量时,回调方法handleMessage();
if (mCallback.handleMessage(msg)) {
return;
}
}
//Handler自身的回调方法handleMessage()
handleMessage(msg);
}
}

handleCallback()方法其实就是调用发送的Runnable中的run()方法:

1
2
3
private static void handleCallback(Message message) {
message.callback.run();
}

分发消息的流程

当Message的msg.callback不为空时,则回调方法msg.callback.run();

当Handler的mCallback不为空时,则回调方法mCallback.handleMessage(msg);

最会调用Handler自身的的回调方法handleMessage(),该方法默认为空,由子类实现。

消息分发的优先级

Message的回调方法:message.callback.run(),优先级最高;
Handler中Callback的回调方法:Handler.mCallback.handleMessage(msg),优先级仅次于1;
Handler的默认方法:Handler.handleMessage(msg),优先级最低。

对于很多情况下,消息分发后的处理方法是第3种情况,即
Handler.handleMessage(msg) ,一般地往往通过覆写该方法从而实现自己的业务逻辑。

Handler有两个重要的特点:

(1)Handler可以在任意线程发送消息,这些消息会被添加到关联的MessageQueue上;

(2) 消息的处理是通过核心方法dispatchMessage(Message msg)与钩子方法handleMessage(Message msg)完成的,handler是在它关联的looper线程中处理消息的。

这就解决了android最经典的不能在其他非主线程中更新UI的问题。android的主线程也是一个looper线程(looper在android中运用很广),我们在其中创建的handler默认将关联主线程MQ。因此,利用handler的一个solution就是在activity中创建handler并将其引用传递给worker thread,worker thread执行完任务后使用handler发送消息通知activity更新UI。

Handler机制工作示意图


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!