Handler 机制

Posted by CoXier on August 4, 2017

一、Handler 机制

Handler机制 是线程间通信很关键的机制,在应用层中,可以利用 Handler 做定时任务、消息统一的转发和处理、View 动画。Handler 消息机制在 java 层涉及:

framework/base/core/java/andorid/os/

  • Handler.java
  • Looper.java
  • Message.java
  • MessageQueue.java

private static Handler mHandler = new Handler(Looper.getMainLooper()){
    @Override
    public void handleMessage(Message msg) {
        Log.d(TAG,msg.obj.toString());
        super.handleMessage(msg);
    }
};

private static final Thread mThread = new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Message message = new Message();
        message.obj = "Hello World";
        mHandler.sendMessage(message);
    }
});

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Log.d(TAG,"Activity onCreate");
    mThread.start();
}

以上就是一个简单的Android异步消机制。

二、Message

因为 Handler 会用到 Message、Looper 和 MessageQueue,所以先从 Message 看起。

Message 作为 Handler 机制的信息载体,内置了两个 int 和 Object 对象用于存储数据。内部数据结构:

数据类型 成员变量 描述
int what 消息的数据类型,每个Handler有自己的命名空间
int arg1,arg2 存储简单数据
Object obj 存储复杂数据
long when 消息触发的时间
Handler target 接受消息
Runnable Callback 处理消息时的回调
Message next 按照when排序的下个消息

文档提醒我们,虽然 Message 的构造方法是 public,但是建议不要直接构造 Message,推荐使用 Meobtain 和 obtain(Message orig),因为 Message 内部维护了一个复用池,避免重复创建 Message。

2.1 obtain message

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

sPool 是「复用池」中的首个空闲 message,当取 message 时,如果池子中有可用的 message,则移动 sPool 到 sPool.next,池子的实际大小减一;否则新建一个 message 。

2.2 recycle message

void recycleUnchecked() {
    // Mark the message as in use while it remains in the recycled object pool.
    // Clear out all other details.
 	// 清理其它数据信息,但是 flags 是在 obtain 时才清除。
    flags = FLAG_IN_USE;
    what = 0;
    arg1 = 0;
    arg2 = 0;
    obj = null;
    replyTo = null;
    sendingUid = -1;
    when = 0;
    target = null;
    callback = null;
    data = null;

    synchronized (sPoolSync) {
      	// MAX_POOL_SIZE 的默认大小为 50
        if (sPoolSize < MAX_POOL_SIZE) {
          	// 将当前被回收的 message 放到池子的首位
            next = sPool;
            sPool = this;
            sPoolSize++;
        }
    }
}

结合 2.1 来看,通过 Obejct sPoolSync 来给 obtain 和 recycleUnchecked上锁,这个上锁方式可以学习借鉴一下。

三、MessageQueue

MessageQueue 在 Handler 机制中,作为一个消息队列,可以让消息「入队」和「出队」。借用 Gityuan 的一句话:

MessageQueue是连接Java层和Native层的纽带,换言之,Java层可以向MessageQueue消息队列中添加消息,Native层也可以向MessageQueue消息队列中添加消息。

3.1 MessageQueue 构造

MessageQueue(boolean quitAllowed) {
    mQuitAllowed = quitAllowed;
  	// mPtr 供 native 方法使用
    mPtr = nativeInit();
}

3.2 「入队」

boolean enqueueMessage(Message msg, long when) {
  	// msg 必须有 handler
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }
  	// msg 同时只能被一个 handler 使用
    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);
          	// 如果当前线程的looper正在退出则回收 msg
            msg.recycle();
            return false;
        }

        msg.markInUse();
      	// msg.when 外部无法直接操作
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        if (p == null || when == 0 || when < p.when) {
          	// 如果 当前 messagequeue 为空,或者 时间小于队首 message 的时间,则插入队首
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else {
			// 按照时间戳来插入 messagequeue
            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;
}

实际上 MessageQueue 并不是一个队列,而是一个链表(不过链表也可以实现队列),将 message 按照触发的时间顺序来入队。

3.3 出队

Message next() {
    // 如果消息循环退出,则直接返回
    final long ptr = mPtr;
    if (ptr == 0) {
        return null;
    }

    int pendingIdleHandlerCount = -1; // 第一次循环时为-1
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
		
      	// 阻塞操作,当等待 nextPollTimeoutMillis 时长返回
        nativePollOnce(ptr, nextPollTimeoutMillis);

        synchronized (this) {
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            if (msg != null && msg.target == null) {
                // 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) {
                    // 当前 message 的触发时间还未到,则在下次循环时阻塞 nextPollTimeoutMillis
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // 获取到下一条 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 {
                // 队列为空
                nextPollTimeoutMillis = -1;
            }

    }
}

四、Looper

Looper 内部有一个 MessageQueue ,通过 MessageQueue 不断的取出 message 。

4.1 Looper 构造

private Looper(boolean quitAllowed) {
    // 创建 MessageQueue
    mQueue = new MessageQueue(quitAllowed);
  	// 将 Looper 与当前线程绑定
    mThread = Thread.currentThread();
}

注意到构造函数是 private ,暴露给外部的是:prepare 方法:

public static void prepare() {
    prepare(true);
}

private static void prepare(boolean quitAllowed) {
    // 一个线程只能调用一次 Looper.prepare() 
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}

通过 ThreadLocal 来保存 looper 对象。ThreadLocal(Thread Local Storage) 线程本地存储区,每个线程都有自己的存储区,不同线程不能访问对方的 TSL 。

4.2 loop

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(); // 见 [3.3]
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

        final long traceTag = me.mTraceTag;
        if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
            Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
        }
        try {
            //  Handler 分发消息 见[5.3]
            msg.target.dispatchMessage(msg);
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        // message 使用完毕,回收
        msg.recycleUnchecked();
    }
}

当调用 looper.loop() 时,looper 就开始不断的取数据,然后交给 Handler 分发。你可能注意到,我们在主线程使用 Handler 机制时,并没有调用 looper.loop() ,其实不然,这些在 ActivityThread已经调用了。

ActivityThread#main


public static void main(String[] args) {
  	// 创建主线程对应的 looper
    Looper.prepareMainLooper();

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

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

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

    // End of event ActivityThreadMain.
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    // 开始取数据
    Looper.loop();

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

五、 Handler

5.1 Handler 构造

Handler 的构造按照我个人的理解可以分为两大类:无 Looper 参数,有 Looper 参数。

5.1.1 Handler 无 looper 参数构造

public Handler() {
  this(null, false);
}

public Handler(Callback callback) {
  this(callback, false);
}

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();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

在无 Looper 构造函数中,Looper 默认是当前线程保存的 Looper 。

  • FIND_POTENTIAL_LEAKS 用来探测当前 Handler 是否是存在内存泄漏的可能性(匿名类、内部类、LocalClass 、非静态)。但是从其定义来看,FIND_POTENTIAL_LEAKS 似乎一直为 false,除非通过反射。
  • 如果 looper 是 null,则 Handler 就无法获取到 message ,所以这里会抛出 RuntimeException

5.1.2 Handler 有 Looper 参数构造

public Handler(Looper looper, Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

到目前为止,我没有设置过 async 为 true,应该用的不是很多。

5.2 发送消息

Handler中总的来看有两种「发送消息」方法,post 和 sendMessage。

post:

  • post(Runnable r)
  • postAtTime(Runnable r, long uptimeMillis)
  • postDelayed(Runnable r, long delayMillis)

sendMessage:

  • sendMessage(Message msg)
  • sendMessageAtTime(Message msg, long uptimeMillis)
  • sendMessageDelayed(Message msg, long delayMillis)

post 操作会把 Runnable 包装成 Message:

private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

post 和 sendMessage 最后都会调用: sendMessageAtTime

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

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    // 设置 msg 的目标是当前的 Handler
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    // message 入队
    return queue.enqueueMessage(msg, uptimeMillis);
}

5.3 Handler#dispatchMessage

在 [4.2]Looper#loop 中看到当取出 message 后,会调用 Handler#dispatchMessage(), 让 Handler 分发该 message。

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

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

public interface Callback {
    public boolean handleMessage(Message msg);
}

处理 message 的优先级:

  • 如果 message 是包装的 Runnable,则调用 runnable
  • 如果构造 Handler 时传入的 Callback 参数不为 null,则调用 Callback
  • 调用重写的 handlerMessage