Android Handler

219 阅读3分钟

Handler MessageQueue Runnable 与 Looper

runable 和 message 可以被压入某个MessageQueue中

Looper循环的去做某件事,不断从MessageQueue中取出一个item,然后传递给handler进行处理。

Handler真正“处理事情”的地方

总结来说就是Looper不断获取MessageQueue中的一个Message,然后由Handler来处理。

Handler源码

每个Thread只对应一个Looper
每个Looper只对应一个MessageQueue
每个MessageQueue中有N个Message
每个Message中最多指定一个Handler来处理事件

Handler的功能

1.处理Messgae
public void dispatchMessage(Message msg) public void handleMessage(Message msg) Looper从MessageQueue中取一个Message后,首先会调用Handler.dispatchMessage()进行消息的派发。 首先判断msg.callback是否为null,不为空的情况下将优先通过callback来处理
判断Handler mCallback是否为空,在不为空的情况下,调用mCallback.handleMessage
如果前面两个对象都不存在那么调用,Handler.handlerMessage。 handlerMessage把Message塞入MessageQueue中。

post系列和send系列

send系列直接处理message
post系列把零散的信息组装成message后发送。

MessageQueue

是一个native方法的队列 包含这些本地方法

  private native static long nativeInit();  初始化队列
  private native static void nativeDestroy(long ptr); 销毁队列
  private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/
  private native static void nativeWake(long ptr);
  private native static boolean nativeIsPolling(long ptr);
  private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);

Looper

Looper分为主线程和普通线程
主线程为ActivityThread
普通线程如下

Looper.prepare();

ThreadLocal是一个特殊的全局变量,只限定于当前线程所处的线程。sThreadLocal保存了新创建的Looper对象

Handler

handler有多种创建函数
public Handler(); public Handler(CallBack callback); public Handler(Looper looper); public Handler(Looper looper,Callback callback);

需要初始化的变量为: MessageQueue mQueue; Looper mLooper; Callback mCallback; 通过以上的mQueue和Looper的mQueue关联,Looper处理到这一情况又会从中调用Handler来进行处理。

ActivityThread

主线程调用的是Looper.prepareMainLooper()和Looper.prepare()
主线程绑定的是ActivityThread.getHandler() 返回的是成员变量mH(Hanlder)

Looper.loop函数

loop函数会一直通过一个死循环一直从MessageQueue中拿消息(通过函数queue.next())。如果消息为null 就返回结束任务,如果有消息来那就分发到handler中处理消息。
next函数如下



    Message next() {
        // Return here if the message loop has already quit and been disposed.
        // This can happen if the application tries to restart a looper after quit
        // which is not supported.
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            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 && 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) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a 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 {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }

                // If first time idle, then get the number of idlers to run.
                // Idle handles only run if the queue is empty or if the first message
                // in the queue (possibly a barrier) is due to be handled in the future.
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler

                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;

            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            nextPollTimeoutMillis = 0;
        }
    }