Android消息机制(Handler,Looper,MessageQueue)-源码分析

Android-消息机制(Handler,Looper,MessageQueue)源码分析

说到消息机制,Android开发者应该都不陌生。在Android开发过程中,需要使用消息传递的场景到处可见,如在子线程更新UI之类常见的操作都离不开消息传递。本篇文章主要从Handler 、Looper以及MessageQueue的运行机制及其源码方面对Android的消息机制进行讲解。

0

Android为了UI组件的访问效率考虑,将UI组件实现为非线程安全的,因此Android不允许在非UI线程中操纵UI组件。但是,当我们需要从网络拉取数据、执行IO操作或者执行一些耗时运算的时候,我们必须将这些任务放到子线程来完成,然而在子线程完成任务后无法操纵UI来与用户交互,给开发者带来了很多麻烦。因此,Android提供了消息机制,通过消息机制我们能很方便的切换任务的执行环境,也即很好的解决了这个问题。

下面是Handler、Looper以及MessageQueue三者之间的关系。在消息传递过程中,首先Handler将消息放到MessageQueue中,然后Looper在死循环中依次从MessageQueue读取消息,并在Looper所在的线程环境中执行相应任务。

Handler

Handler是Android消息机制的上层接口,通过Handler我们能将Message发送给Looper去处理,也即将指定的任务切换到指定的线程环境中去执行。

首先看一下Handler的两个主要构造方法:

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

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

Handler的两个主要构造方法中一个不需要Looper对象(使用线程默认Looper,下面会详解),另一个则需要一个Looper对象,而Handler发送的消息就在该Looper中进行处理。

因此,在一个Handler创建的时候,会获取与之关联的Looper,以及该Looper内的MessageQueue。

Handler的post方法(post发送对Runnable对象会被包装到Message的callback)以及send方法最终都是通过调用sendMessageAtTime(Message msg, long uptimeMills)方法实现的,该方法源码如下:

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) {
    msg.target = this;   
    if (mAsynchronous) {        
        msg.setAsynchronous(true);
    }    
    return queue.enqueueMessage(msg, uptimeMillis);
}

而enqueueMessage方法里又简单的调用了MessageQueue的enqueueMessage方法,该方法将Message插队到MessageQueue的队列里(其实是单链表实现),下面会详细分析。

再看一下Handler的dispatchMessage方法,当Looper处理Message时,会将Message发送给Handler的该方法来处理:

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

该方法的处理流程为:首先看该Message是不是一个Runnable(也即通过post方法发送的,Runnable被包装到callback),如果是,则在handlerCallback方法中执行该Runnable;如果不是则看该Handler的Callback是否被设置,设置了callback的话就交给callback处理;如果callback没有成功处理,也即返回了false,或者没有设置callback,那么该Message交给Handler的handlerMessage方法(也即一般在我们创建Handler时覆盖的方法)处理。

由上可见,Handler的主要工作就是发送Message到MessageQueue,那么,消息又是怎么被Looper处理的呢?

Looper

我们知道,UI线程在初始化的时候,会创建一个Looper,该Looper负责处理外部事件,因此,我们在主线程中使用Handler时不用在创建Looper。但是,在子线程中,我们必须先使用Looper.prepare()来创建一个Looper,然后调用Looper.loop()来开启消息循环,之后才能正常的使用Handler传递消息。Looper的构造方法是私有的,整个Looper类只有三百行,下面主要看一下prepare、myLooper以及loop方法:

    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));
    }
private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

首先,看Looper的prepare方法,该方法会创建一个Looper(创建Looper的时候会创建MessageQueue),并将其放入到ThreadLocal中(一种线程内数据的存储对象,后面会详解)。如果你在主线程调用Looper.prepare()将会得到RuntimeException:“Only one Looper may be created per thread”。

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

myLooper方法会返回当前线程的Looper对象,该对象在调用prepare方法时被放在ThreadLocal中,使用Handler类中无Looper的构造方法创建Handler时会使用此方法获取线程内的Looper。

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

loop方法的代码比较长,但不难看出,loop方法中开启了一个死循环,在循环中依次读取Message,然后调用msg.target.dispatchMessage(msg)方法,msg.target为发送该Message的Handler,也即调用了Handler的dispatchMessage方法。在介绍Handler时已经说过Handler处理Message的流程, 此处不在赘述。

值得一提的是,loop中的死循环跳出的唯一条件是queue.next()返回null,而queue.next()返回null表示该MessageQueue正在退出,也即该Looper正在退出。此外,如果MessageQueue中没有消息,则它的next方法会产生阻塞,直到有消息可读。

通过这样,不管Handler在哪个线程环境,Handler都能将Message发送给关联的Looper,存储到Looper中的MessageQueue中, 然后Looper在自己被创建的线程环境中,通过死循环方法loop来从MessageQueue中读取消息,然后使用Handler规定的处理方式来处理消息。

到这,大家应该理解了Handler的工作机制了。但是,在Handler与Looper之间,MessageQueue发挥着重要的作用,下面再看一下Message Queue。

MessageQueue

MessageQueue是Handler发送的消息的目的地,是Looper读取消息的来源。MessageQueue中的Message以单链表结构组织在一块(Message对象本身具有next数据域,正是通过此数据域来组织成单链表)。下面主要看一下enqueueMessage方法以及next方法。enqueueMessage方法将一个Message插入到单链表中,next方法返回并删除一个Message。

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

首先看一下enqueueMessage方法, 该方法比较长,但逻辑很简单。首先检查Message的完整性,然后判断MessageQueue是否正在退出,是的话就抛出异常。没问题的话,就将该Message插入到单链表中(按照when的顺序)。

然后看一下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;
        }
    }

该方法也比较长,但是主要逻辑很清楚,首先,使用一个native方法nativePollOnce来阻塞获取消息,然后判断消息是否是ready状态,也即now < msg.when是否成立,然后返回这个Message,或者执行IdlerHandler(如果有的话,可以通过MessageQueue的addIdleHandler方法添加)。

通过上面MessageQueue的两个主要方法,我们知道了MessageQueue的工作机制(核心工作是在native方法中完成,此处不再分析),也即通过enqueueMessage将Message插入到链表中,然后使用next方法依次从链表中读取并删除消息,最后消息交给Looper来处理。

ThreadLocal

分析完Handler、Looper以及MessageQueue的工作机制,大家应该对Android的消息传递有了一些了解,但是不知道大家有没有注意到一个问题,那就是,当我们使用Handler的没有Looper参数的那个构造方法构造对象时,Handler与哪个Looper关联呢?对,是与当前线程的Looper对象关联, 也即通过Looper.myLooper()来获取到的当前线程的Looper对象。那么问题来了,myLooper是一个静态方法,也就是说在不同的线程环境里调用myLooper方法,myLooper方法内操纵的都是同一个(或同多个)静态数据域,那么是怎么做到在不同的线程环境里调用myLooper方法返回线程相关的Looper对象呢?答案是ThreadLocal。

首先看一下在Looper中出现过的get和set 方法(在Looper的prepare方法中会使用set方法,在myLooper中会使用get方法)。

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

这是set方法,首先获取当前的线程对象啊,然后使用getMap方法获取当前线程对象中的ThreadLocalMap对象,该对象是Thread对象的局部变量,也即任何一个线程中都会有ThreadLocalMap。然后使用ThreadLocalMap的set方法来存储value(下面详解)。

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

然后再看一下get方法,和set方法类似,get 方法也先取得当前线程对象的ThreadLocalMap对象,然后使用map.getEntry方法来获取一个Entry,Entry的Value属性即为最终结果。

现在,已经可以很清晰的知道get、set方法是如何工作了:首先获取当前线程的ThreadLocalMap,然后通过该对象存取数据。ThreadLocalMap是ThreadLocal的内部类,现在看一下ThreadLocalMap的set方法以及getEntry方法:

        private void set(ThreadLocal key, Object value) {

            // We don't use a fast path as with get() because it is at
            // least as common to use set() to create new entries as
            // it is to replace existing ones, in which case, a fast
            // path would fail more often than not.

            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);

            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                ThreadLocal k = e.get();

                if (k == key) {
                    e.value = value;
                    return;
                }

                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }

            tab[i] = new Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }

Entry[]为ThreadLocalMap中存储数据的数组,Entry存储了ThreadLocal的弱引用以及与该ThreadLocal相对应的Object。set函数通过hash映射将该key对应的value放到Entry数组中。

private Entry getEntry(ThreadLocal key) {
            int i = key.threadLocalHashCode & (table.length - 1);
            Entry e = table[i];
            if (e != null && e.get() == key)
                return e;
            else
                return getEntryAfterMiss(key, i, e);
        }
        private Entry getEntryAfterMiss(ThreadLocal key, int i, Entry e) {
            Entry[] tab = table;
            int len = tab.length;

            while (e != null) {
                ThreadLocal k = e.get();
                if (k == key)
                    return e;
                if (k == null)
                    expungeStaleEntry(i);
                else
                    i = nextIndex(i, len);
                e = tab[i];
            }
            return null;
        }

getEntry函数同样通过hash映射来取得相应的Entry,遇到冲突则使用getEntryAfterMiss函数顺次查找。查找不到指定数据则返回null。

总的来说,ThreadLocal其实是将自身作为key,存储的对象作为value,然后将这个key-value(也即Entry)放到ThreadLocalMap中,而ThreadLocalMap内部通过对key进行hash映射来将value放到数组的指定位置。这样,由于每个Thread中都有一个自己的ThreadLocalMap,而ThreadLocal做为存储的key,因此同一个ThreadLocal对象在不同的Thread中能关联不同的value。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值