解读Handler源码

Handler概述

Handler是Android消息机制的上层接口,最常见的应用就是通过Handler在子线程更新UI。

从创建Handler对象实例入手

  • 创建Handler对象
private Handler mHandler = new Handler(new Handler.Callback() {
@Override
    public boolean handleMessage(Message msg) {
          return false;
        }
});    

在这里覆写了 handleMessage()方法。

  • 点进去看Handler的构造函数,发现以上代码调用的一个参数的Handler构造方法,在内部调用了两个参数的构造方法。
  • mLooper = Looper.myLooper(); Handler构造函数的第一行有效代码,调用了Looper类的静态方法获取Looper实例。点进去的具体代码如下:
public static @Nullable Looper myLooper() {
      return sThreadLocal.get(); 
}
> Return the Looper object associated with the current thread.  Returns null if the calling thread is not associated with a Looper.

从注释可以看出,这个返回的Looper是每一个不同线程的Looper对象,也就是说不同的线程获得的Looper都是相互没有干扰的副本。
那么是如何实现同一数据类型的不同线程副本,具体分析`sThreadLocal.get()`这行代码
#### ThreadLocal ####
* 首先sThreadLocal是一个ThreadLocal的对象,先来一小demo:
    public static void main(String[] args) {
        final ThreadLocal test = new ThreadLocal<>();
        
        test.set(1);
        System.out.println(Thread.currentThread().getName() + "当前值 = " +test.get());
        
        new Thread(){
            public void run() {
                test.set(2);
                System.out.println(Thread.currentThread().getName() + "当前值 = " +test.get());
            };
        }.start();
        
        new Thread(){
            public void run() {
                test.set(8);
                System.out.println(Thread.currentThread().getName() + "当前值 = " +test.get());
            };
        }.start();
        
        new Thread(){
            public void run() {
                System.out.println(Thread.currentThread().getName() + "当前值 = " +test.get());
            };
        }.start();
    }
    打印结果:
main当前值 = 1
Thread-0当前值 = 2
Thread-1当前值 = 8
Thread-2当前值 = null
从代码和结果中可以看出,ThreadLocal类中有set/get方法,不同的线程获取的数据都与其自己设置的相同,同时如果没有调用set方法设置,返回的结果就是null,也就是说不同的线程拥有互不干扰的对象副本。

ThreadLocal工作原理

    现在来看ThreadLocal的源码,分析它到底是如何实现的功能。先看set方法:
public void set(T value) {
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values == null) {
            values = initializeValues(currentThread);
        }
        values.put(this, value);
    }
  • Values values = values(currentThread)返回了一个localValues,这里涉及到ThreadLocal中的一个静态类ThreadLocal.Values。
  • 接下来进行判断,如果localValues为null,则调用initializeValues方法进行初始化,否则会调用put方法将value存进去。
void put(ThreadLocal key, Object value) {
        cleanUp();

        // Keep track of first tombstone. That's where we want to go back
        // and add an entry if necessary.
        int firstTombstone = -1;

        for (int index = key.hash & mask;; index = next(index)) {
            Object k = table[index];

            if (k == key.reference) {
                // Replace existing entry.
                table[index + 1] = value;
                return;
            }

            if (k == null) {
                if (firstTombstone == -1) {
                    // Fill in null slot.
                    table[index] = key.reference;
                    table[index + 1] = value;
                    size++;
                    return;
                }

                // Go back and replace first tombstone.
                table[firstTombstone] = key.reference;
                table[firstTombstone + 1] = value;
                tombstones--;
                size++;
                return;
            }

            // Remember first tombstone.
            if (firstTombstone == -1 && k == TOMBSTONE) {
                firstTombstone = index;
            }
        }
    }

可以看出,ThreadLocal的值就保存在LocalValues内部名为table的Object数组中,Threadlocal的reference字段后面紧跟着value,在这里也就是我们传进来的Looper。接下来就看看get方法:

 public T get() {
        // Optimized for the fast path.
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values != null) {
            Object[] table = values.table;
            int index = hash & values.mask;
            if (this.reference == table[index]) {
                return (T) table[index + 1];
            }
        } else {
            values = initializeValues(currentThread);
        }

        return (T) values.getAfterMiss(this);
    }

get方法先根据当前线程获取其中的ThreadLocal.Values,从values中得到Object数组table,最后从table中找到存储的对象。

  • 接下来继续分析Handler的构造方法,获取Looper对象之后会判断,获取的Looper对象是否为null,如果为null,则抛出异常。
if (mLooper == null) {
    throw new RuntimeException(
        "Can't create handler inside thread that has not called Looper.prepare()");
}

从异常的内容不难看出,在创建handler对象之前,必须要调用Looper.prepare()方法对Looper对象初始化。

  • mQueue = mLooper.mQueue;接着又从上一步获取的当前线程的Looper实例中获取了其保存的MessageQueue(消息队列),这样就保证了handler的实例与我们Looper实例中MessageQueue关联上了。
  • mCallback = callback;保存了创建Handler实例时覆写的callback回调方法。
  • mAsynchronous = async;保存了创建Handler实例时对异步的设置。
  • 以上就创建handler实例的代码,显然已经成功了。其实具体实现又要看looper内部的实现,以下来分析looper类。

looper

  • 在以上创建Handler实例的时候,提到必须要先调用Looper.prepare()方法进行初始化,以下代码:
 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));
}

在代码中,sThreadLocal是一个ThreadLocal对象,也就是对Looper对象进行了保存,同时进行了一次判断,判断sThreadLocal是否为null,否则抛出异常,这也就说明了Looper.prepare()方法不能被调用两次,同时也保证了一个线程只有一个Looper实例。

  • 先看new Looper(quitAllowed)Looper的构造方法:
 private Looper(boolean quitAllowed) {
            mQueue = new MessageQueue(quitAllowed);
            mThread = Thread.currentThread();
}
1). 创建了一个消息队列`new MessageQueue(quitAllowed);`
2). 获取了当前线程实例。

核心方法loop()

  • 现在让我们才看看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;
        }

        // This must be in a local variable, in case a UI event sets the logger
        Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }

        msg.target.dispatchMessage(msg);

        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }

        // Make sure that during the course of dispatching the
        // identity of the thread wasn't corrupted.
        final long newIdent = Binder.clearCallingIdentity();
        if (ident != newIdent) {
            Log.wtf(TAG, "Thread identity changed from 0x"
                    + Long.toHexString(ident) + " to 0x"
                    + Long.toHexString(newIdent) + " while dispatching to "
                    + msg.target.getClass().getName() + " "
                    + msg.callback + " what=" + msg.what);
        }

        msg.recycleUnchecked();
    }
}

在代码开头,调用Looper类的静态方法直接返回了sThreadLocal存储的Looper实例,如果me为null则抛出异常,也就是说looper()方法必须在prepare()之后方能运行。紧接着获取到了Looper实例中的mQueue(消息队列),之后就进入了无限循环。从消息队列中取出一条消息,如果没有消息就阻塞。
关键代码->msg.target.dispatchMessage(msg);把消息交给了msg的target的dispatchMessage()方法去处理。点开target,可以发现target其实就是handler对象,最后释放消息占据的资源msg.recycleUnchecked();

加入消息队列

好了说了这么多,那么消息是怎么加入到消息队列MessageQueue中的呢

  • 那么现在具体看看平时使用的sendMessage(Message msg)方法具体是如何实现的。
public final boolean sendMessage(Message msg)
{
       return sendMessageDelayed(msg, 0);
}
  • 其中的sendMessageDelayed方法
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
  • 最后还是调用了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);
}

enqueueMessage方法中

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

在进入方法的第一行,enqueueMessage中首先为meg.target赋值为this,也就是把当前的handler作为msg的target属性。最终会调用queue的enqueueMessage的方法,同时可以看出最终调用了queue.enqueueMessage方法,那么queue又是什么呢,其实就是MessageQueue的对象实例,也就是说handler发出的消息,最终会保存到消息队列中去。

  • 接下来再来看dispatchMessage方法:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

就会发现其中的handleMessage(msg);方法中是空的,其实就是需要我们覆写的最终消息处理方法。

在这里还有一个问题,在Activity中,我们并没有显示的调用Looper.prepare()和Looper.loop()方法,结果Handler还是成功的创建了呢,其实这个因为在Activity的启动代码中,已经在当前的UI线程调用了Looper.prepare()和Looper.loop()方法。

最后对handler机制做一个总结:

  • 首先Looper.prepare()在本线程保存一个Looper实例,然后在该实例中创建并保存了一个MessageQueue对象,同时因为Looper.prepare()在一个线程中只能调用一次,所以MessageQueue在一个线程只会存在一个。
  • Looper.loop()方法会让当前线程进入一个无限循环,不断的从MessageQueue的实例中读取消息,然后回调msg.target.dispatchMessage(msg)方法。
  • 而target就是Handler的实例,那么再看Handler的构造函数,其首先得到了当前线程保存的Looper实例,进而又从Looper实例中获取了保存的MessageQueue实例,与其相关联。
  • 接下来Handler的sendMessage方法,会给msg的target赋值为handler本身,然后加入到MessageQueue中。
  • 那么回过头来看Looper.loop()获取到了消息调用的dispatchMessage(msg)方法其实最终调用了我们覆写的handleMessage方法。

学习的时间不长,有什么不对还请指出,谢谢指教。

你可能感兴趣的:(解读Handler源码)