Android Handler 机制

作者: 乐之飞于 | 来源:发表于2017-08-25 16:35 被阅读81次

    Handler 是什么

    官网解释:A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.

    Handler 的用途(主要的两个用途)

    1、线程间的调度。主要有 sendEmptyMessage(int), sendMessage(Message), sendMessageAtTime(Message, long),sendMessageDelayed(Message, long) 这些方法发送消息,并通过 Handler 中的 handlerMessage() 方法接受处理返回过来的消息。

    2、延时处理消息或者执行某些操作。主要有 post(Runnable), postAtTime(Runnable, long), postDelayed(Runnable, long)这些方法。

    Handler 相关的类

    Message:消息类,包含一个描述和任意数据对象,用于 Handler 发送和接受。虽然Message 的构造函数是 public,但是一般是使用 Message.obtain() 或者 Handler.obtainMessage() 来获取 Message 实例。

    MessageQueue: 消息集合,用于 Looper 分发的一个 low_level 类。

    Looper:被用于消息循环线程的类。死循环从 MessageQueue 中取 Message。

    ThreadLoacl:用于保存和提供线程级别的变量,主要是用来将 Looper 绑定到相关的线程上的。

    Handler 工作流程

    Handle 的初始化

    一般情况下我们回去创建一个类继承 Hanlder 并实现其中的 handleMessage 来接收处理消息

    class MyHandler extends Handler{
        
        public MyHandler(Looper looper){
            super(looper)
        }
        
        @Override
        public void handleMessage(Message msg) {
                //接受处理消息
        }
    }
    

    然后创建 MyHandler 的实例并通过其发送消息

    MyHandler myHandler = new MyHandler(looper);
    ......
    Message message = Message.obtain();
    //初始化message
    ......
    myHanlder.sendMessage(message);
    

    在主线程中创建 Handler 实例可以直接通过Handler hander = new Handler()来实现,因为主线程中已经存在 Looper, Hanlder 内部可以通过 Looper.myLooper()来拿到 Looper的实例。

    Handler 的构造函数最终会分为两种:
    1、有 Looper 参数的情况:

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

    2、没有 Looper 参数的情况:

    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 为 null 创建 Handler是会失败的
        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;
    }
    

    通过 Handler 的构造器可以知道,Handler 中的 MessageQueue 的实例 mQueue 是通过 Looper 拿到的。还有一个 Looper.myLooper() 的方法,后面说。

    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
       mThread = Thread.currentThread();
    }
    

    通过 Looper 的构造器可以知道 MessageQueue 的实例是由 Loop而创建。

    消息的发送

    从 Handler 的定义可以知道,Handle 可以发送 Message 和 Runnable 对象到消息队列。从 Handler 源码可以跟到发送 Message 会调用下面方法:

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

    然后调用了 enqueueMessage 方法:

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        //这里把当前 Handler 实例赋给了 Message 的 target 变量,用于后期的消息分发
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
    

    这里回去调用 MessageQueue 的 enqueueMessage方法:

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

    至此 由 Handler 发送的消息已经加到 MessageQueue 当中。那么由 Handler 发送 Runnable 对象是怎样的?Handler 中的 postDelayed 的的代码:

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

    由此可知采用的是和发送消息一样的流程。Handler 会通过 * getPostMessage* 方法提供一个 Message 对象:

    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        //这里把 Runnable 对象赋给了 Message 中的 callback 变量,后面的消息分发会用
        m.callback = r;
        return m;
    }
    

    消息的循环

    消息分发是通过 Looper 实现的,所以先说下一下 Looper 这个类:

    Looper 的构造函数是私有的,所以我们只能通过 Looper.myLooper() 来获取当前线程的 Looper 实例:

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

    方法中并没有去 new 一个 Looper 实例,而是通过 sThreadLocal 去 get。这里的 sThreadLocal 是 ThreadLocal 的一个实例。get 方法:

    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null)
                return (T)e.value;
        }
        return setInitialValue();
    }
    

    这里会先去获取当前的线程,然后通过当前线程去拿 ThreadLocalMap 的实例(ThreadLocalMap 就是个 Map,通过Key-Value存取值)。这个map实例是在 Thread 中的,但在 Thread 中并没有初始化,初始化是在 ThreadLocal 中进行的。如果 map 为空,回去进行初始化的操作,在初始化过程中回去 new 一个 ThreadLocalMap 的实例赋给 Thread 中的 ThreadLocalMap 变量。 如果返回的值为 null 的话,说明 Looper 还没有初始化。 使用 Looper.prepare() 对 Looper进行初始化:

    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 对象并且 set 到 sThreadLocal 中去,这样 sThreadLocal 就会保留这个 Looper 对象。

    关于 ThreadLocal ,主要是用来将 Looper 绑定到相关的线程上的。在 Android 系统中主线程是非安全线程,如果 Looper 没有绑定到主线程中(其他线程不能修改UI)是会抛出异常的。

    消息分发是通过 Looper 的 loop 方法进行的,可以通过Looper.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(); // 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 traceTag = me.mTraceTag;
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            try {
                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();
        }
    }
    

    通过 MessageQueue 的 next() 或获取 Message:

    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 (false) Log.v("MessageQueue", "Returning message: " + msg);
                        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("MessageQueue", "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;
        }
    }
    

    在这个方法中也是采用死循环去取队列中的消息的,这里使用的是 Native 方法获取消息,涉及到的是 Linux 的的一些阻塞机制(不懂)

    获取到的 Message 对象通过 Message 的 target 来分发消息,这里的 target 就是发送该 Message 实例的 Handler。最终通过 Handler 中的 dispatchMessage 方法来分发消息:

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

    前文说过,Handler 如果发送的是 Runnable 对象,会存到 msg.callback 中,如果 Runnable 不为空,就是执行 handleCallback:

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

    即执行 Runnable 中的 run 方法。

    如果发送的是 Message 对象,那么最终执行的就是 handleMessage,该方法接受到Message 对象并进行处理。至此,消息从发送到接受处理就完成。

    相关文章

      网友评论

        本文标题:Android Handler 机制

        本文链接:https://www.haomeiwen.com/subject/tezudxtx.html