Handler探究

2017-06-10  本文已影响8人  Jansid

在项目中经常用Handler,然而一直没有深究其原理,今天就来探究一下这个熟悉的陌生人。
提到Handler就需要提到LooperMessage了,这三者就像刘关张一样,是拜把子的兄弟。
先介绍一下他们三个:
Handler: 句柄,负责向MesageQueue消息队列中发消息和处理消息
Looper: 循环器,不断从MessageQueue消息队列中取消息
Message: 消息

接下来看看Handler的原理,我们知道Android的应用是由事件驱动运行的。Java程序的入口函数是main()函数,Android应用也是一样,Android应用启动也是从main()函数开始,这个函数在ActivityThread中。
我们来看看main()方法的内容:

        ...上面省略
        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()从命名看出它的作用因该是准备一个主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();
        }
    }

从名字可以看出,这个方法的作用是用于准备主Looper

接下来看prepare(false)的源码:

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 变量,我们来看看它是干什么用的

// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

在成员变量中找到了它的定义,发现这是ThreadLocal类型的变量,那么,ThreadLocal是干什么的呢?
看到它的get() set()方法,感觉它应该是一个容器。接下来我们来验证一下,先看看set()方法:

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 我们查看这个类的注释发现有这么一句:
ThreadLocalMap is a customized hash map suitable only for maintaining thread local values.
说明这是一个自定义的HashMap容器(改进了HashMap的算法,以使得排列更加均匀)
那么就可以很清晰看到这里是将ThreadLocal作为键,Looper作为值存储起来。从而得出结论,ThreadLocal的作用就是将Looper与当前线程(主线程)关联起来,并且保证Looper的唯一(线程安全)。
这样looper()方法就看完了,塔的作用就是创建一个Looper并将其与主线程关联。
接下来看看最后一行的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);
            } finally {
                ···
            }
        ···
            msg.recycleUnchecked();
        }
    }

可以看到,这里先是获取了一个Looper对象,这里的myLooper()的代码很简单,就是调用了ThreadLocalget()方法,获取Looper对象,如果这里报了异常,也就是说之前没有调用Looper.prepare()方法。这里就不多说了
接下来就是获取消息队列。
然后就是一个死循环,遍历消息队列中的消息。queue.next()也就是一直获取下一个消息。如果消息队列中没有消息则退出。
接下来看msg.target.dispatchMessage(msg);,看到后面的dispatchMessage(msg)发现这个方法有点熟悉,这不是Handler里面的方法么? 我们来看看Messagetarget究竟是什么👻?
我们在属性中看到了它: Handler target;,这就很清晰了。
接下来我们看看Handler的dispatchMessage(msg)方法

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

首先看这个方法的名字,可以知道它的作用是分发消息
先看msg.callback,这个callback又是什么呢?通过查看Message的属性,看到Runnable callback;,那么msg.callback就是一个Runnable类型的变量了。
接下来看handleCallback(msg);

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

发现这里会调用Runnablerun()方法。这个时候大脑灵光一闪,我们平常通过Handler发消息时使用的handler.post(Runnable)最终是不是通过这里回调的run()方法呢?答案是肯定的,后面再分析
继续看,mCallback是一个接口,里面有一个抽象方法,如果我们在创建Handler的时候实现了这个接口,就会在这里回调了。
比如:

Handler dHandler = new Handler(new Handler.Callback() {
    @Override public boolean handleMessage(Message msg) {
        return false;
    }
});

继续看,如果handleMessage(msg);,这里就不用解释了。
举例:

Handler dHandler = new Handler() {
    @Override public void handleMessage(Message msg) {
      
    }
};

这样我们就能理解loop()方法了,就是不断的循环,从消息队列中取出消息,通过回调让Handler处理消息。
loop()方法最后一行msg.recycleUnchecked();这个的意思就是回收消息。
前面说了,Android应用是由事件驱动的,应用在运行时不断的从消息队列中拿消息处理消息,那么我们如果每发一个消息就new一个对象显然是不行的,因此,在MessageQueue中有一个消息池,消息是用链表方式存放的,消息池中的消息不断被使用-回收。

再来看看我们平常使用Handler发消息的用法:

Handler handler = new Handler();
Runnable run = new Runnable() {
    @Override public void run() {
    }
};
handler.post(run);

通过查找post()方法的源码,发现里面调用的是sendMessageDelayed(getPostMessage(r), 0);方法,我们来看看它的源码:

public final boolean sendMessageDelayed(Message msg, long delayMillis) {
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

发现这里面调用了另一个方法,继续追溯到sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);方法:

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(queue, msg, uptimeMillis)方法

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
}   

这里可以看到最后是插入消息,将Handler发的消息插入到消息队列中并且会将发消息的Handler绑定到Message中。在将消息插入消息队列时会附加一个时间,而消息队列里面的消息就是按这个时间来排序。前面说了,应用启动了之后会不断从消息队列中取消息,而我们用Handler发送消息的时候,消息会被插入到消息队列中,然后Looper一直从消息队列中取消息,并将Message通过其绑定的Handler回调处理。

Handler事件发送和处理
上一篇下一篇

猜你喜欢

热点阅读