Handler、Looper、MessageQueue、ThreadLocal

根据一个简单的Demo,分析一波

package com.example.yanlong.aidlclient;

import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "客户端";
    private Handler handler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView textView = (TextView) findViewById(R.id.textView);
        textView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Message obtain = Message.obtain();
                obtain.arg1 = 10;
                handler.sendMessage(obtain);
            }
        });

        new Thread("自定义线程") {
            @Override
            public void run() {
                Looper.prepare();
                handler = new Handler() {
                    @Override
                    public void handleMessage(Message msg) {
                        Log.i(TAG, Thread.currentThread().getName());
                        Log.i(TAG, msg.arg1 + "");
                    }
                };
                Looper.loop();
            }
        }.start();
    }

}
点击后

01-15 22:21:43.452 20028-20046/com.example.yanlong.aidlclient I/客户端: 自定义线程
01-15 22:21:43.452 20028-20046/com.example.yanlong.aidlclient I/客户端: 10

分析

1、Looper.prepare();

看说明,不用死扣,简单来说就是,给当前线程创建一个Looper,你可以去创建一个 handler引用,开始循环前记得调用一下 loop();函数,结束它可以调用 quit();函数

 /** Initialize the current thread as a looper.
  * This gives you a chance to create handlers that then reference
  * this looper, before actually starting the loop. Be sure to call
  * {@link #loop()} after calling this method, and end it by calling
  * {@link #quit()}.
  */
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.set(new Looper(quitAllowed));

/**
 * Sets the current thread's copy of this thread-local variable
 * to the specified value.  Most subclasses will have no need to
 * override this method, relying solely on the {@link #initialValue}
 * method to set the values of thread-locals.
 *
 * @param value the value to be stored in the current thread's copy of
 *        this thread-local.
 */
public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

主要是 方法内使用了 Thread t = Thread.currentThread();切换到当前线程,然后通过当前线程t,获取 t 的内部变量,map对象内有Entry数组,这个我们看 

if(map != null)

map.set(this,value)

/**
 * Set the value associated with key.
 *
 * @param key the thread local object
 * @param value the value to be set
 */
private void set(ThreadLocal<?> key, Object value) {

    // We don't use a fast path as with get() because it is at
    // least as common to use set() to create new entries as
    // it is to replace existing ones, in which case, a fast
    // path would fail more often than not.

    Entry[] tab = table;
    int len = tab.length;
    int i = key.threadLocalHashCode & (len-1);

    for (Entry e = tab[i];
         e != null;
         e = tab[i = nextIndex(i, len)]) {
        ThreadLocal<?> k = e.get();

        if (k == key) {
            e.value = value;
            return;
        }

        if (k == null) {
            replaceStaleEntry(key, value, i);
            return;
        }
    }

    tab[i] = new Entry(key, value);
    int sz = ++size;
    if (!cleanSomeSlots(i, sz) && sz >= threshold)
        rehash();
}
这里就好理解了,局部变量 tab = table,那么 table是什么,在ThreadLocal构造函数里

重点:初始化必须是2的倍数,主要用来做位运算的。原理同HashMap

/**
 * The initial capacity -- MUST be a power of two.
 */
private static final int INITIAL_CAPACITY = 16;

ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
    table = new Entry[INITIAL_CAPACITY];
    int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
    table[i] = new Entry(firstKey, firstValue);
    size = 1;
    setThreshold(INITIAL_CAPACITY);
}
这里看 初始化 一个 Entry数组,然后 当前的 ThreadLocal 放在数组哪里呢?通过位运算 得出 i,为什么是位运算而且是 15 呢

因为 15 的二进制是 1111,那么 ThradLocal 的hash值 位运算 1111 后,得出的值完全取决于hash值后四位,如果ThreadLocal的hash值分布平均,那么这里得出的值就是平均的,这个计算方法同样在 HashMap内应用到。

计算出 ThreadLocal 的位置,然后放进去。

代码里的for 循环写的很漂亮

for (int i = nextIndex(staleSlot, len);
     (e = tab[i]) != null;
     i = nextIndex(i, len))
private static int nextIndex(int i, int len) {
    return ((i + 1 < len) ? i + 1 : 0);
}
可能这时候有同学会想到,如果是纯数组,没有链表的情况下,加入 数组长度 16,这16个都放满了,我再放第17个时候,循环不就出问题了吗 ?下面代码不就不对了吗?

大家考虑多了,这个不会到你说的那地步,因为在你放不满的时候,就扩容了,具体扩容时机,我们看下触发条件。

/**
 * The number of entries in the table.
 */
private int size = 0;

int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
    rehash();

size 数值在 类初始化时已经定义 为 1

/**
 * Set the resize threshold to maintain at worst a 2/3 load factor.
 */
private void setThreshold(int len) {
    threshold = len * 2 / 3;
}

threshold数值也在 类初始化时定义,为 16 * 2 / 3 = 10

所以结果是,如果没有需要清理掉的弱引用,并且当前已经塞入ThreadLocal 的数量 > 10

进行rehash();那我们计算一下 rehash 的触发条件是 10/16  也就是 0.75,和 hashMap挺巧合

rehash()

/**
 * Re-pack and/or re-size the table. First scan the entire
 * table removing stale entries. If this doesn't sufficiently
 * shrink the size of the table, double the table size.
 */
private void rehash() {
    expungeStaleEntries();

    // Use lower threshold for doubling to avoid hysteresis
    if (size >= threshold - threshold / 4)
        resize();
}
/**
 * Expunge all stale entries in the table.
 */
private void expungeStaleEntries() {
    Entry[] tab = table;
    int len = tab.length;
    for (int j = 0; j < len; j++) {
        Entry e = tab[j];
        if (e != null && e.get() == null)
            expungeStaleEntry(j);
    }
}
删除陈旧条目

这里解释一波

重点:e.get() 得到的是 ThreadLocal对象,这个对象是弱引用在 Entry中的,所以如果在代码中对 ThreadLocal 设置 == null;那么GC回收了这个键值,其对应的value 也就失去了意义。

每删除一个,size --

static class Entry extends WeakReference<ThreadLocal<?>> {
    /** The value associated with this ThreadLocal. */
    Object value;

    Entry(ThreadLocal<?> k, Object v) {
        super(k);
        value = v;
    }
}

然后判断如果这时候的 size >= 10 - 10/4;也就是 这时候 剩下 还能用的 ThreadLocal  >= 8 那么久进行 reSize。

可见代码严谨性。 

看说明,翻倍。

/**
 * Double the capacity of the table.
 */
private void resize() {
    Entry[] oldTab = table;
    int oldLen = oldTab.length;
    int newLen = oldLen * 2;
    Entry[] newTab = new Entry[newLen];
    int count = 0;

    for (int j = 0; j < oldLen; ++j) {
        Entry e = oldTab[j];
        if (e != null) {
            ThreadLocal<?> k = e.get();
            if (k == null) {
                e.value = null; // Help the GC
            } else {
                int h = k.threadLocalHashCode & (newLen - 1);
                while (newTab[h] != null)
                    h = nextIndex(h, newLen);
                newTab[h] = e;
                count++;
            }
        }
    }

    setThreshold(newLen);
    size = count;
    table = newTab;
}

2、new Looper(true);

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));
}
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}
Looper内创建了 MessageQueue

3、创建  Handler

/**
 * Default constructor associates this handler with the {@link Looper} for the
 * current thread.
 *
 * If this thread does not have a looper, this handler won't be able to receive messages
 * so an exception is thrown.
 */
public Handler() {
    this(null, false);
}
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对象,以及 mQueue对象。

这两个在 Looper.prepare();中已经建好了。

4、Looper.loop();

开启消息队列,网上介绍一堆,这里就不写了。

5、Handler.sendMessage();

Message obtain = Message.obtain();
obtain.arg1 = 10;
handler.sendMessage(obtain);
new Thread("自定义线程") {
    @Override
    public void run() {
        Looper.prepare();
        handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                Log.i(TAG, Thread.currentThread().getName());
                Log.i(TAG, msg.arg1 + "");
            }
        };
        Looper.loop();
    }
}.start();
看一下 handler.sendMessage一步步都到了哪里

/**
 * Pushes a message onto the end of the message queue after all pending messages
 * before the current time. It will be received in {@link #handleMessage},
 * in the thread attached to this handler.
 *  
 * @return Returns true if the message was successfully placed in to the 
 *         message queue.  Returns false on failure, usually because the
 *         looper processing the message queue is exiting.
 */
public final boolean sendMessage(Message msg)
{
    return sendMessageDelayed(msg, 0);
}
/**
 * Enqueue a message into the message queue after all pending messages
 * before (current time + delayMillis). You will receive it in
 * {@link #handleMessage}, in the thread attached to this handler.
 *  
 * @return Returns true if the message was successfully placed in to the 
 *         message queue.  Returns false on failure, usually because the
 *         looper processing the message queue is exiting.  Note that a
 *         result of true does not mean the message will be processed -- if
 *         the looper is quit before the delivery time of the message
 *         occurs then the message will be dropped.
 */
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
/**
 * Enqueue a message into the message queue after all pending messages
 * before the absolute time (in milliseconds) <var>uptimeMillis</var>.
 * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
 * Time spent in deep sleep will add an additional delay to execution.
 * You will receive it in {@link #handleMessage}, in the thread attached
 * to this handler.
 * 
 * @param uptimeMillis The absolute time at which the message should be
 *         delivered, using the
 *         {@link android.os.SystemClock#uptimeMillis} time-base.
 *         
 * @return Returns true if the message was successfully placed in to the 
 *         message queue.  Returns false on failure, usually because the
 *         looper processing the message queue is exiting.  Note that a
 *         result of true does not mean the message will be processed -- if
 *         the looper is quit before the delivery time of the message
 *         occurs then the message will be dropped.
 */
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);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}
最后调用的是 queue.enqueueMessage();

可以得出以下结论,

死循环的是谁,是 Loop方法,

死循环做什么,读取 mQueue队列是否有消息

读出消息给谁,给Handler去分发处理

谁把消息赛进队列,是mQueue

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;
    }
msg.target.dispatchMessage(msg);

在Message类中

/*package*/ Handler target;





猜你喜欢

转载自blog.csdn.net/qq_26030147/article/details/79070315