教你看源码之入门系列:最简单的源码 ----Handler 源码解读

948 阅读9分钟

一些Android开发同学在初级阶段都停留在调用API的层面上,在调用了一系列Api之后做出一些很炫酷的功能或者是很轻松地就完成了一些需求的独立开发时感到非常的满足,这很OK,但很多时候都忽略了这些功能仅仅停留在Api的调用上,任何一个刚入门的开发都能在查找一些资料后通过调用api完成一些复杂的功能,但这仅仅是会调用而已,长期停留在这种阶段虽然也能完成一些业务上的需求,但对个人能力来说是一种很大的阻碍,所以对于Android Api,光会调用还是太low了,明白Api的设计思路和相关原理并借鉴到自己的开发中,才能让我们不断进步,并且和Apier们区分开来~今天从最简单的Handler开始来玩一玩源码。 Handler可以说是各位开发Boy最常用的工具之一,异步回调主线程必备利器,但很多做技术的同学只停留在使用的阶段,特别是一些初级开发者,Handler内部具体是怎么操作的,涉及的一些其他模块的内容,以及和其他异步执行工具之间的关系,如AnsyncTask,RxJava等有啥关系,都是一知半解,今天就来通俗易懂的扒一扒Handler的源码。

Handler这个类大概有七八百行代码,其实还是一个很简单的类,Google工程师写的代码就是简洁,但也有很多可以学习的地方,看源码也是提高个人能力的很重要的一部分,当然,仅仅是看懂并没有什么卵用,学习别人的设计思想和设计理念再运用到我们的开发中才是最有价值的。

很多人讨厌看源码,代码是别人写的,看起来真是麻烦,不过,这才是看源码的价值,其实看源码是很有技巧的,带着问题看源码,比较自己的设计思路和别人的差距,从而学习或者灵活地借鉴才是正确的姿势~ 那么在看Handler源码之前,我们先思考一下如果是自己该如何设计Handler这样的功能呢。举个很简单的例子,怎么在子线程计算某个结果然后显示在界面上的TextView,如果把这样一个简单的处理看做一条消息的话,这就涉及到消息传递,消息维护,消息处理和线程之间切换调度的问题等,Handler就是解决这些问题的一工具类而已。我们带着这些问题去看Handler源码就会很有目标和方向性了。 那么在进入正题之前先思考这几个问题:

1.Handler的使用场景,主线程与非主线程该如何使用Handler

2.Handler是如何与Looper交互的,Looper如何和线程关联

3.Looper管理MessageQueue的机制,Message的调度和处理是怎么运作起来的。

首先来个最经典的Handler的用法,这里不考虑内存泄露的问题。

private static final int TAG = 0x99;
final Handler mHandler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                if(msg.what == TAG){
                    tvResult.setText("Work Done");
                }
            }
        };
        new Thread(new Runnable() {
            @Override
            public void run() {
                //Time-consuming tasks
                mHandler.sendEmptyMessage(TAG);
            }
        }).start();
        

代码很简单,子线程完成耗时任务然后通知主线程更新UI,这是Handler最常见的用法,首先看Handler的构造方法,我们用的是无参的构造,

public Handler() {
        this(null, false);
    }
    

最后再跟到这个构造方法里

public Handler(Callback callback, boolean async) {
        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;
    }
    

可以看到,我们开始传入了一个null的Callback,async的值也是false,Callback是最后处理Message的逻辑,我们先不管async, 看看Callback:

public interface Callback {
        public boolean handleMessage(Message msg);
    }
    

这个Callback中的方法和我们前面在创建Handler的时候写的是一样的,其实我们实现重写Handler的handlerMessage方法和在构造方法里传入Callback效果都是一样的,看源码就知道

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

dispatchMessage在Message最后被处理的时候会被调用到,Callback先说到这,继续回到构造方法里可以看到有Looper,Queue,显然Handler的正常运转离不开Looper和Queue,如果Looper为null,直接会挂掉。有的同学就会说了,我们刚才写的代码并没有用到Looper,为什么可以正常使用呢,先来研究下Looper吧。

Looper源码一开始就说明了Looper要如何和Handler使用:

* 
*  class LooperThread extends Thread {
*      public Handler mHandler;
*
*      public void run() {
*          Looper.prepare();
*
*          mHandler = new Handler() {
*              public void handleMessage(Message msg) {
*                  // process incoming messages here
*              }
*          };
*
*          Looper.loop();
*      }
*  }
*/

在使用Handler之前需要调用Looper.prepare(),为Handler提供一个Looper,并且还需要调用Looper.loop()来启动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));
        }

可以看到sThreadLocal.set(new Looper(quitAllowed));在当前线程中保存了一个新的Looper,并且每个线程只能拥有一个Looper,所以,在每个线程调用Looper.prepare()就可以给每个线程准备一个Looper了,这里涉及到一个很关键的东西,就是ThreadLocal,ThreadLocal是一个可以为每一个线程保存不同数据的工具,在每个线程调用sThreadLocal.set(new Looper(quitAllowed))可以为每个线程都保存一个Looper,这里先看Looper的构造方法里都干了啥:

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

这里把当前线程和MessageQueue与Looper绑定起来了,将新的Looper放入ThreadLocal中, 并且在不同的线程调用sThreadLocal.get()获取到的Looper都是不一样的。所以不同线程的Handler都需要有一个Looper,那么Looper的工作时什么呢,继续来看 Looper.loop()。

public static void loop() {
    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        msg.target.dispatchMessage(msg);
        msg.recycleUnchecked();
    }
    }

我们把一些不关键的代码剔除之后再来看看。looper里有一个死循环,从一个queue里不断的去读取内容,直到读取到的msg为null的时候才跳出循环, msg.target.dispatchMessage(msg),这里的msg.target就是我们发送msg的handler,比如上面Handler经典栗子中的mHandler.sendEmptyMessage(TAG),这里的msg就是封装了TAG的Message,而msg.target就是mHandler。然后调用mHandler的dispatchMessage,这就是上面的那个dispatchMessage,里面直接将调用到我们构造方法里传入的Callback或者上文中重写的handleMessage().

说到这里,Looper的功能就很明白了,先调用Looper.prepare()给线程添加一个Looper,再启动Looper.loop()后持续监控一个维护的Queue,在合适的时机将Queue中的消息传给相应的Handler处理。那么如果Queue里没有Message如何呢,queue.next()在没有消息时将会阻塞,直到有消息到来再继续读取,并且queue的next()是带有删除效果的,一旦消息被出队列之后就会被删除。这里的queue满足队列的先进先出,具体实现可以参考我们前面分析Looer 源码的方式试试仔细看看MessageQueue的源码,体验一下带着问题看源码的方式,所以暂时不细说。

刚才还剩下一个问题没有说明,为什么在主线程上使用Handler不用我们去手动调用Looper的两个方法来启动Looper呢,因为主线程是默认自带Looper的,这点可以从Looper里窥探一二。 来看Looper中有几个比较特殊的变量和方法:

private static Looper sMainLooper;  // guarded by Looper.class
/**
 * Initialize the current thread as a looper, marking it as an
 * application's main looper. The main looper for your application
 * is created by the Android environment, so you should never need
 * to call this function yourself.  See also: {@link #prepare()}
 */
 public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
    }
/** Returns the application's main looper, which lives in the main thread of the application.
 */
 public static Looper getMainLooper() {
    synchronized (Looper.class) {
        return sMainLooper;
    }
    }

可以看到Looper里有一个sMainLooper,光从名字就可以知道是为主线程提供的,再看看prepareMainLooper(),从源码中的注释就能很清楚的看到系统为主线程提供了Looper,在prepareMainLooper()被调用后主线程就默认带有一个在工作中的Looper了。那这个方法是在何时被调用的呢,因为看注释这个方法被调用不止一次的时候也会出问题,所以调用的实际也很重要。我们可以看看程序的入口,我们的ActivityThread.class里,在它的main()方法中,可以找到我们要的东西:

public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        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());
        AndroidKeyStoreProvider.install();
        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);
        Process.setArgV0("");
        Looper.prepareMainLooper();
        ActivityThread thread = new ActivityThread();
        thread.attach(false);
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }
        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
    }

在启动主线程的时候调用了Looper.prepareMainLooper(),所以我们在主线程上使用handler 是不用手动调用Looper了。

那么,消息是如何被Looper和MessageQueue发送和处理的部分将的差不多了,最后看看结合起handler是如何工作的。 首先我们在生成handler的时候,提供一个处理Msg的逻辑,这也是最后Message具体内容处理的位置。并且在生成Handler之前保证相应线程Looper存在并且能够正常工作,这时Looper能够不断监视一个MessgeQueue,之后我们可能需要处理一些异步的工作,让某个线程持有该handler的引用,这就是线程切换的关键,这样可以让其他线程调用handler最终将异步的信息传递给handler所在的线程。接下来就是做一些耗时任务或其他操作,并在完成之后将一些信息封装成Message,然后再使用我们的handler,发送message即可。此时message 会被放入MessageQueue,如果相关Looper检测到了message的到来,并且相关性线程并未阻塞,就把message从队列里放出来,再传递给handler,进而调用handler的dispatchMessage完成message的处理,这样就完成了线程之间的切换。再 回过头来看真实清晰明了了。