Android Handler消息机制源码分析

一、概述

如果要使用handler发送消息,每一个线程都应该初始化一个Looper对象,调用Looper对象的loop方法,循环的从MessageQueue中取得Message,然后通过Handler的dispatchMessage方法处理此消息,一般的,此方法的最终逻辑会执行我们实例化Handler对象时,重写的handleMessage方法里的逻辑代码。这里涉及到了消息机制的几个核心类:Handler,Message,MessageQueue,Looper。

二、源码分析

一般我们首先会实例化一个Handler的对象,所以先看Handler的构造方法:

public Handler(@Nullable Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class 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 " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

关键在这两句代码,mLooper = Looper.myLooper()拿到了looper的对象, mQueue = mLooper.mQueue拿到了MessageQueue的对象,然后先看Looper的myLooper方法:

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

它是通过ThreadLocal的一个get存根获取到的Looper的对象,那么这个对象是在什么时候设置呢?我们知道每个线程都必须初始化一个Looper对象,不然会报错:Can't create handler inside thread that has not called Looper.prepare(),那么我们先看一下UI线程是怎么初始化Looper对象的,在ActivityThread里:

public static void main(String[] args) {
        ......

        Looper.prepareMainLooper();

        // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
        // It will be in the format "seq=114"
        long startSeq = 0;
        if (args != null) {
            for (int i = args.length - 1; i >= 0; --i) {
                if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                    startSeq = Long.parseLong(
                            args[i].substring(PROC_START_SEQ_IDENT.length()));
                }
            }
        }
        ......

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

        ......
        Looper.loop();

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

我们发现有一个 Looper.prepareMainLooper()的方法,它的核心逻辑如下:

public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

  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的prepare方法,在这个方法里,给ThreadLocal设置了一个Looper的对象,也就相当于把一个线程同一个Looper对象绑定在了一起,而通过Looper的构造方法我们知道了MessageQueue的对象是在初始化Looper对象的时候实例化的:

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

需要说明的是,在主线程里API已经调用了Looper.prepare方法,但是在子线程里没有调用,所以我们需要自己手动调用Looper.prepare方法。接下来我们了解一下Looper到底是怎么从MessageQueue里拿消息的,通过ActivityThread的main方法,我们知道了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;

       ......

        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

           ......
            try {
                msg.target.dispatchMessage(msg);
                if (observer != null) {
                    observer.messageDispatched(token, msg);
                }
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } catch (Exception exception) {
                if (observer != null) {
                    observer.dispatchingThrewException(token, msg, exception);
                }
                throw exception;
            } finally {
                ThreadLocalWorkSource.restore(origWorkSource);
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            ......
        }
    }

这是一个死循环,循环的从MessageQueue里取出消息,并通过 msg.target.dispatchMessage(msg)处理拿到的消息,这个msg.target其实就是handler对象,那么handler的对象是在什么时候赋值给message的target属性的呢?我们来看handler的sendMessage方法,也就是发送消息必须调用的方法,最终会调用enqueueMessage方法,代码如下:

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

这里就是把自身的实例赋值给了message的target,然后通过MessageQueue的enqueueMessage方法,把此消息压入队列当中:

boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }

        synchronized (this) {
            ......
            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 {
                ......
                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;
            }
           ......
        }
        return true;
    }

此处会按照 Message 的时间 when 来有序得插入 MessageQueue 中,可以看出 MessageQueue 实际上是一个有序队列,只不过是按照 Message 的执行时间来排序。至此,整个消息机制就已经基本分析完毕,下面讲一些面试相关的问题。

三、一些问题

首先是handler的post和sendMessage 的区别,post方法源码如下:

public final boolean post(@NonNull Runnable r) {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

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

public void dispatchMessage(@NonNull 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();
    }

这里可以看出,post方法把传入的Runnable 对象设置给了Message的callback,而在handler的dispatchMessage方法里,如果 Message 的 Callback 不为 null,一般为通过 post(Runnabl) 方式,会直接执行 Runnable 的 run 方法。因此这里的 Runnable 实际上就是一个回调接口,跟线程 Thread 没有任何关系。

其次就是Looper.loop() 为什么不会阻塞主线程,刚才我们了解了,Looper 中的 loop 方法实际上是一个死循环。但是我们的 UI 线程却并没有被阻塞,反而还能够进行各种手势操作,这是为什么呢?在 MessageQueue 的 next 方法中,有如下一段代码:

for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            nativePollOnce(ptr, nextPollTimeoutMillis);
         ......
   }

nativePollOnce是一个本地native方法,当调用此 native 方法时,主线程会释放 CPU 资源进入休眠状态,直到下条消息到达或者有事务发生,通过往 pipe 管道写端写入数据来唤醒主线程工作,这里采用的 epoll 机制,我也不是很清楚这个机制的具体逻辑,有知道的同学可以补充一下。

你可能感兴趣的:(Android Handler消息机制源码分析)