Handler详解

Read the fucking source code!

还是源码精妙


当我们面试的时候,经常遇到这样一题,请描述一下Handler的工作方式

然后我们会说,Handler机制包括Handler,Message,MessageQueue,Looper。

Handler会发送Message给MessageQueue,Looper会不断的从Message中取出一个Message,然后调用Handler的handleMessage方法。

当然,还可以说的更详细一点,不过最近感觉,只是了解了使用方法不行,了解了工作机制还是不行,要深入的了解源码,才能感觉到一个优秀的操作系统的伟大之处。

首先我们说一下这几个的数量关系:

一个线程有一个Looper,一个Looper内部有一个MessageQueue,一个MessageQueue有多个Message,并且一个MessageQueue可能有多个Handler的引用,一个Message只对应一个Handler

关于Looper

主线程的Looper

我们都知道一个线程有一个Looper,那么这个怎么实现的呢,我们看源码。

Looper.java


static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    private static Looper sMainLooper;  // guarded by Looper.class

    final MessageQueue mQueue;
    final Thread mThread;

首先看第一行 有一个ThreadLocal变量,一个线程维护一个ThreadLocal副本,互不干扰,这点大家应该都清楚。因为这个sThreadLocal是static final的,所以我们在任何线程都能拿到这个变量。

第二行有个变量sMainLooper,唯一标识UI线程的Looper,这个Looper怎么实例化的呢,在我们的Application入口处:

 Looper.prepareMainLooper();
        if (sMainThreadHandler == null) {
            sMainThreadHandler = new Handler();
        }

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

        Looper.loop();

我们看到了在这里的“ Looper.prepareMainLooper()”

我们看一下这个方法都干了什么:

Looper.java


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

第一行“prepare”,我们应该有印象,在子线程里构造Looper,我们会用到Looper.prepare(),其实prepare()方法内部就是调用了这个方法:

Looper.java

public static void prepare() {
        prepare(true);
    }

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了,就抛出异常

我们看到当传入参数为false的时候,就会执行Looper(quitAllowed)这个构造方法,我们来看一下这个构造方法:

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

这一段揭示了Looper和MessageQueue和Thread的关系:Thread有一个Looper,一个Looper有一个MessageQueue!

在这个构造方法中就实例化了一个MessageQueue,这个就是主线程的MessageQueue,mThread就是主线程。

调用构造函数之后,就把一个Looper加入到了sThreadLocal中了。

最后一行调用了myLooper():

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

这个方法就是返回当前的Looper实例。

子线程的Looper

上面我们说,如果子线程想拿到一个Looper要调用Looper.prepare(),这个方法又会调用prepare(true),当参数为true时,我们也调用了Looper()的含参构造方法,至于这个参数什么意思,下面再说。就会把新的looper加入到sThreadLocal中。

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;

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

这一部分的代码很长,总的来说,就是开启了一个无限循环,不断的从MessageQueue中拿消息,

如果没有消息,就会退出,上面说 queue.next(); // might block,下面我们再来分析MessageQueue。

小结:怎么在子线程和主线程拿到Looper我们搞清楚了,Looper怎么和MessageQueue对应起来的,下面慢慢说。

MessageQueue

MessageQueue在哪实例化的呢,在这:

Looper.java

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

下面来看MessageQueue的构造方法。

MessageQueue.java

 MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
    }

这个变量看名字应该是标记MessageQueue是否可以退出。

Message

一个Message只属于一个Handler,这个在下面会说。

其他三个只是处理消息的媒介,这个才是真正的消息内容,我们看Message都有什么实例变量:

 public int what;
 public int arg1;
 public int arg2;
 public Object obj;

常用的就是这几个,what和obj我们最常用。

what就是用户自定义的标识Message的标记,在Handler处理的时候,可以根据这个what做具体的工作。

obj是Message携带的唯一一个对象。

实例化一个Message对象的时候,最好的方法是调用Message.obtain();

public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

spool应该是Message链中的最后一个消息,然后取出这个消息返回,这一段自己没有看太懂,想深入了解的同学可以自己查一下。

小结:Message大致的用法就是这样,比较简单主要是注意要习惯用Message.obtain来获取消息。下面我们来看Handler。

Handler

Handler和Message的关系

一个Message只属于一个Handler,我们看一个Message怎么和一个Handler关联起来的,我们通常发送消息用Handler.sendMessage,我们看这个方法:

Handler.java

public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }

其实调用了这个方法sendMessageDelayed(msg, 0);msg是我们要发送的消息,0延迟时间,单位毫秒。我们看一下这个方法:

public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return 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);
    }

这一段就是将Handler发送的message送到Handler对应的Looper的MessageQueue中。

我们看一下enqueueMessage这个方法:

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

找了那么久,终于找到了,其实就是这个msg.target = this,每个Message的Target就是在这设置的。

我们还可以重复利用Message,用这个方法Handler.obtainMessage(int what, Object obj)。

Handler.java

public final Message obtainMessage(int what, Object obj)
    {
        return Message.obtain(this, what, obj);
    }

Handler和MessageQueue的关系

上面我们说发送Message的时候,会将这个Message发送到Handler对应的MessageQueue中,这个MessageQueue怎么来的呢?

我们知道MessageQueue实际上在Looper里,我们看这个Looper怎么得到的:

Handler.java

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

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

我们看到这个Looper是通过Looper.myLooper()这个方法来的,如果当前线程没有Looper实例,就会抛出异常,所以在子线程实例化Handler的时候,就必须调用Looper.prepare();

当然这也不是唯一的选择,看另一个构造方法:

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

我们可以传入一个Looper,这个Looper通常是主线程的Looper或者是HandlerThread的Looper。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容