Handler的简单回顾

我们都知道,Android中更新ui界面都需要在主线程中的完成,如果在子线程中更新ui会导致程序崩溃,但是如果在子线程中获取到了数据,需要吧数据展示到界面上,此时我们怎么来解决呢,我们很自然想到了Handler, 首先在ui线程创建一个Handler对象,然后在子线程中创建一个Message对象,借助的ui线程创建的Handler发送出去,最后在Handler的handlerMessage()中进行message对象的处理和ui的更新。

子线程中创建Handler出现的问题

但我们在子线程中直接创建handler后,运行程序,会崩溃,打印log,会发现错误信息为Can't create handler inside thread that has not called Looper.prepare(),
意思是我们当前线程没调用Looper.prepare()方法是不能创建Handler对象的,所以我们在子线程中调用Looper.prepare后创建的Handler对象的。

new Thread(new Runnable(){
	@override
	public void run(){
		Looper.prepare(); // 在创建Handler对象前调用此方法
		Handler handler = new Handler();
	}
})

从源码看,为什么在子线程创建Handler前必须调用Looper.prepare()方法,而主线程就不用。

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

如果mLooper == null,就会抛出以上所出现的异常,而mLooper是通过Looper.myLooper()得到的,我们查看myLooper()的源码

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

发现sThreadLocal中获取Looper实例,那么我们在Looper类中查看Looper.prepare()方法

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实例对象,则会创建一个,并设置到ThreadLocal中,并且每个线程只能有一个Looper实例对象,否则出现Only one Looper may be created per thread 的错误提示。

到这里我们会想到一个新问题,我们在Main Thread中创建Handler对象的时候并没有调用Looper.prepaer()方法,那为什么没有错误提示?

我们查看ActivityMainThread类中的main(String args)方法

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

        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);

        Environment.initForCurrentUser();

        // Set the reporter for event logging in libcore
        EventLogger.setReporter(new EventLoggingReporter());

        Security.addProvider(new AndroidKeyStoreProvider());

        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);

        Process.setArgV0("<pre-initialized>");

        Looper.prepareMainLooper();  // main thread在程序启动时,系统已经帮助我们调用了Looper.prepareMainLooper()方法

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

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

我们看到,在程序启动的时候,系统已经帮助我们调用了Looper.prepareMainLooper()方法,我们点开prepareMainLooper()方法查看,

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

}

我们看到在Looper.prepareMainLooper()同样调用了looper.prepare()方法来为ui线程创建一个Looper对象。
到这里,Handler的创建我们已经理解了。

Handler如何发送消息

首先,new 出一个Message对象,或者通过Handler的obtainMessage()从消息池中获取一个Message对象,然后使用setData()或者obj携带一些数据,接着借助Handler发送出去即可。

###Handler消息发出到接受的过程

消息的入队

Handler中有多个发送消息的方法,其中除了sendMessageAtFrontOfQueue之外,其他的发送消息的方法都会调用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);
    }

这个方法有俩个参数,其实中msg是我们发送的对象,而uptimeMillis则表示发送消息的时间,它的值是自系统开机时间到当前的的时间的毫秒数,再加上需要i的延迟时间,而最终这俩个参数又都被传到enqueueMessage()方法中,那么这个MessageQueue又是什么呢,其实它是一个队列,Handler发送的所有的消息都要先进入这个队列, 它有入队和出对的方法, MessageQueue对象实在Looper的构造,因此一个Looper对应了一个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;
    }

MessageQueue并没有使用集合把所有的消息都保存起来,它只用了一个mMessage对象表示当前待处理的消息,然后看上面的源码,我们呢就会发现,所谓的入队列,其实就是把所有的消息按照时间来排序,即uptimeMills参数。具体的操作方法就是根据时间的顺序调用mesg.next,从而为每一个消息指定它的下一个消息是什么。
如果我们调sendMessageAtFrontOfQueue()方法来发送消息,它也是调用enqueueMessage来让消息入列,但是它的时间为0,这时会把mMessages赋值为新入列的这条消息,然后将这条消息的next指定为刚才的mMessages,这样就实现了完成添加消息到列头部的操作。

消息的出队

看完消息的入队,接下来继续看消息的出队,我们来看Looper.loop()方法的源码: