Handler、Looper、ThreadLocal 、Values

来源:互联网 发布:哪个运营商的4g网络快 编辑:程序博客网 时间:2024/06/03 13:05

1.Looper创建过程

Looper主要方法如下:
- prepareMainLooper()
- prepare()
- loop()
- Looper(boolean quitAllowed)

  Looper的创建过程我们就从应用程序的主线程(UI线程)的Looper的创建过程说起。UI进程的创建是在ActivityThread的main()的主方法开始的,代码如下:

    public static void main(String[] args) {       //looper不相关代码已移除 ①        Looper.prepareMainLooper();        ActivityThread thread = new ActivityThread();        thread.attach(false);        if (sMainThreadHandler == null) {            sMainThreadHandler = thread.getHandler();        }        //启动looper循环 ②        Looper.loop();        throw new RuntimeException("Main thread loop unexpectedly exited");    }

  main()方法中①处调用了Looper.prepareMainLooper()方法。prepareMainLooper()的内部实现较为简单,实际调用了Looper.prepare()方法,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、Thread关系紧密的对象成员ThreadLocal。sThreadLocal.get()将获取一个ThreadLocal对象(文章下面记录get()方法的内部实现)。并判断获取的对象是否为null,如非null将抛出异常”Only one Looper may be created per thread”此处也是保证一个线程只有Looper对应的关键点。代码④中new Looper()实例并存储到Values中。

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

  在Looper构造方法中创建了存放Message实例的消息队列并将当前所在线程赋值给自己的mThread变量。

  在代码②处调用了Looper.loop()方法。loop()具体实现如下:

    public static void loop() {        //⑥myLooper()内部调用了ThrealLocal的get方法()        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;        //实现为一个死循环不断从Looper的MessageQueue消息队列中去除消息并处理        for (;;) {            Message msg = queue.next(); // might block            if (msg == null) {                // No message indicates that the message queue is quitting.                return;            }            //无关代码以去除            //⑦msg.target为发送消息的Handler实例            //在此调用了handler的dispatchMessage()            msg.target.dispatchMessage(msg);            // 无关代码以去除        }

  在调用Looper.loop()方法之前必须先调用Looper.prepareMainLooper()方法。然后从获取的looper实例中获取消息队列对象并启动一个死循环不断的从消息队列中取得消息。在代码⑦处是Handler消息处理的关键,Message对象中持有一个发送这个消息的handler实例的一个引用。msg.target是一个Handler实例,在此调用msg.target.dispatchMessage(),在dispatchMessage()方法内部实际上是调用了Handler的handleMessage()方法。文章下面在介绍Handler时会介绍他们的具体实现。

2.ThreadLocal

主要方法如下:

  • get()
  • set()

Implements a thread-local storage, that is, a variable for which each thread has its own value. All threads share the same {@code ThreadLocal} object, but each sees a different value when accessing it, and changes made by one thread do not affect the other threads. The implementation supports {@code null} values.

  大致意思是ThreadLocal实现了线程本地存储。所有的线程都共享同一个ThreadLocal对象,每个线程都有一个自己的值。每个线程在访问这个值时不影响其他线程相对应的值。且这个值允许为空。

  ThreadLocal为编写多线程并发程序提供了一个新的思路。如下图所示,我们可以将ThreadLocal理解为一块存储区,将这一大块存储区分割为多块小的存储区,每一个线程拥有一块属于自己的存储区,那么对自己的存储区操作就不会影响其他线程。对于ThreadLocal,则每一小块存储区中就保存了与特定线程关联的Looper。

image

Thread、ThreadLocal和Values的关系

  Thread的成员变量中有类型为ThreadLocal.Values的localValues变量,由于线程特定变量可能会有多个,并且类型不确定,所以ThreadLocal.Values有一个table成员变量,类型为Object数组。这个localValues可以理解为二维存储区中与特定线程相关的一列。
ThreadLocal类则相当于一个代理,真正操作线程特定存储区table的是其内部类Values。

image
image
  ThreadLocal和Values的关系和原理在上面已经做了简要说明,那么继续从代码④处说起。在ActivityThread的main()方法中第一次创建肯定是会执行代码④的逻辑。在此暂时忽略new Looper(quitAllowed)的内部实现来看一下sThreadLocal.set()到底做了什么:

    public void set(T value) {        //获取当前线程        Thread currentThread = Thread.currentThread();        /**        *获取当前线程中持有的Values对象        *values(Thread current)直接返回了        *current.localValues对象        */        Values values = values(currentThread);        if (values == null) {            values = initializeValues(currentThread);        }        //⑤        values.put(this, value);    }    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);    }

Values内部实现

  在第一次绑定looper对象前value值肯定为null,上述set()即会执行initializeValues()。内部实现直接new了一个Values对象且负值给了Thread.localValues对象。在Values()的构造方法中初始化了内部Object[]的大小(默认大小是16 * 2,Object[] table中交叉存储Key和value)和其他一些初始化。

  代码⑤Values.put(ThreadLocal

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

  从put()方法的实现逻辑可以明白Values中Object[] table是如何在工作:他的存储结构如下:
这里写图片描述

  在table数组中是交叉存储ThreadLocal和value值的。假设i处存储ThreadLocal实例那么i + 1处就会存储和ThreadLocal实例结对的value值。上图中Object实例的位置就是存放looper实例的位置。

Handler

主要方法如下:
- handleMessage(Message msg)
- dispatchMessage(Message msg)
- sendMessageAtTime(Message msg, long uptimeMillis)

在Android系统中只有Main thread才能更有应用程序UI界面。所以要将耗时任务放到子线程中执行,否则会产生ANR错误(UI进程中执行任务超过5秒回报错)。任务在子线程中执行完毕需要更新UI界面,一般情况下我们会采用Handler来接收子线程的Message消息在主线程中更新UI界面。

  Handler一般我们主要用于实现在子线程中处理耗时任务后更新UI界面的功能。通过重写handleMessage(),并在其中实现操作UI更新的逻辑。Handler类有多个构造方法,每个方法内部实现都不复杂就不纠结了。

  • public Handler()
  • public Handler(Callback callback)
  • public Handler(Looper looper)
  • public Handler(Looper looper, Callback callback)

第一个和第二个构造函数中没有传入Looper,这两个构造函数都将通过调用Looper.myLooper()获取当前线程绑定的Looper对象。
“`java
public Handler() {
this(null, false);
}

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

“`
通过这个Looper实例获取了其中保存的MessageQueue(消息队列)。每个Handler对应一个Looper对象,产生一个MessageQueue.

#### Callback
  第二个和第四个构造函数还传递了Callback对象,Callback是Handler中的内部接口,需要实现其内部的handleMessage方法,Callback代码如下:
java
public interface Callback {
public boolean handleMessage(Message msg);
}

  使用Handler有两种方式:第一种继承Handler类并实现其handleMessage();第二种在构建Handler对象时参数中传入Callbck的实现类对象。在出入Callback对象的情况下会优先调用Callback的实现。具体逻辑会在dispatchMessage()方法中看到。源码如下:
java
public void dispatchMessage(Message msg) {
//⑧Message的Runnable
//在此暂时不做介绍
if (msg.callback != null) {
handleCallback(msg);
} else {
//⑨使用Callback的实现类来处理消息
//这种情况下不会再走handler的handleMessage()
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//⑩在没有Message.Runnableh和Callback实现的情况下
handleMessage(msg);
}
}

从dispatchMessage()的实现中可知在Message携带Runnable的实现时就是调用Runnable的run方法。在没有runnable实现的情况下如果存在Callback的实现会调用Callback.handleMessage()方法。在两者都没有时会最终调用handler的handleMessage()。

由此可见三者的优先级关系为Message.Runnable > Callback.handlerMessage() > Handler.handleMessage();
#### 发送消息
Handler发送消息的方法众多,其中大部分方法最终会调用Handler的sendMessageAtTime()方法。sendMessageAtTime()的具体实现如下:
java
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);
}

从上边代码可以看出sendMessageAtTime()最后也是调用了enqueueMessage()让消息进入队列:
java
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//⑪消息将入消息队列
return queue.enqueueMessage(msg, uptimeMillis);
}

其实消息的发送本质上就是将Message按照时间进行排序后放入到MessageQueue中去

这部分代码看过几次不是很理解,就记录了下来。其中有部分是之前做笔记摘录的,不知出处如有不妥请告知。以上个人理解不当之处请务必告知,谢谢

原创粉丝点击