Android Handler机制

 

1. ThreadLocal

在研究Handler的机制之前我们想明确一个知识点,ThreadLocal. ThreadLocal字面意思是线程本地变量,他保存的是一个变量的副本,并且只对当前线程可见。我们做个例子:

final ThreadLocal<Object> threadLocal = new ThreadLocal<>();
    for(int i = 0; i < 10; i ++){
        new Thread(new Runnable() {
            @Override
            public void run() {

                String local = "Thread" + Math.random();
                Log.i(TAG, "local1:" + local);
                threadLocal.set(local);
                Log.i(TAG, "local2:" + threadLocal.get());

            }
        }).start();
    }

结果如下:

可以看到第一个和第二个打印的日志是一样的,也可能你觉得这个不能说明啥,一会我们看看源码也许你就明白了。 OK那我们看看他的源码:

    public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

在set值得时候,拿到当前的线程,并拿到一个map,就这个值存放到map中。这个TheaLocalMap是什么呢?

/**
 * Get the map associated with a ThreadLocal. Overridden in
 * InheritableThreadLocal.
 *
 * @param  t the current thread
 * @return the map
 */
ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

就一句话,还是从当前的线程里面去拿的,继续看下threadLocals:

 /* ThreadLocal values pertaining to this thread. This map is maintained
 * by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;

那么这个ThreadLocalMap 是一个什么东西呢,继续往下看:

 static class ThreadLocalMap {

    /**
     * The entries in this hash map extend WeakReference, using
     * its main ref field as the key (which is always a
     * ThreadLocal object).  Note that null keys (i.e. entry.get()
     * == null) mean that the key is no longer referenced, so the
     * entry can be expunged from table.  Such entries are referred to
     * as "stale entries" in the code that follows.
     */
    static class Entry extends WeakReference<ThreadLocal<?>> {
        /** The value associated with this ThreadLocal. */
        Object value;

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

可以看到这个ThreadLocalMap中有一个存储结构用来存储键值对,这个value其实就是我们set进去的变量,继续看下set方法:

 /**
     * 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();
    }

这个方法也不复杂,就是看看当前的数组table中是不是已经存过了,如果已经存过了那么久覆盖掉之前的Value,没有的话就创建一个Entry放到数组中。所以同一个ThreadLocal在一个线程中只能存放一个值,不然会被覆盖掉。

当然拿到当前的值时候也不难,明白了这个get方法也就很简单了。 总结下 ThreadLocal的作用 当前线程是绑定一个变量,只能当前线程访问,别的线程访问不了也修改不了他的值。在多线程的环境中, 当多个线程需要对某一个变量进行频繁的操作,但又不需要同步的时候,可以用ThreadLocal,因为他存储在当前的线程中的,效率恨到,其实就是空间换时间。

2. Handler Looper, MessageQueue, Message

Handler Looper MessageQueue Message他们之间关系其实不难理解,我们new一个Handler并实现里面的handMessage方法,参数是一个message, 然后用Handler发送message,发送的message到了MessageQuueue这个队列里面,Looper维持一个死循环,一直从MessageQueue里面取Message,之后调用相应的Handler里面的handMessage方法,这样就完成了一个循环。 说起来你不难,但是要理解里面的机制,还需要看源码: 首先一个应用在启动的时候,系统会调用ActivityThread.java中的main方法,在main方法里面会去初始化Looper。

    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();

可以看到调用了Looper.prepareMainLooper();这个方法其实就是初始化了一个Looper,这个Looper是给主线程调用,接着下面调用了Looper.loop() 这个方法是开启一个死循环,一会再看,先看下Looper.prepareMainLooper()

 /**
 * Initialize the current thread as a looper, marking it as an
 * application's main looper. The main looper for your application
 * is created by the Android environment, so you should never need
 * to call this function yourself.  See also: {@link #prepare()}
 */
public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

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

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

在上面的源码prepare中new了一个Looper并把它放到了ThreadLocal中,上面我们已经说了,ThreadLocal存储的变量和线程绑定在一起,那么当前的线程其实是主线程。 sMainLooper 这变量其实就是我们常用的Looper.getMainLooper().

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

那么MessageQueue是什么时候创建的,其实就是new Looper的时候创建的。

我们平常用Handler的时候都是new Handler 如下:

private Handler mHandler = new Handler(){
    @Override
    public void handleMessage(Message msg) {

        switch (msg.what){
            case xx
                break

        }
        super.handleMessage(msg);
    }
};
mHandler.sendEmptyMessageDelayed(1, 100);

那么Handler发送的的message是怎么到队列里面,并且之后是怎么回调的,继续看源码:

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

所有的send方法都会掉到上面这个方法里面来,然后插进队列:

  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将会绑定当前发送的Handler,这也就为以后出来message埋下了伏笔,其实说白了就是拿到这个target指向的Handler来调用handmessage方法处理message。

那你有没有注意到那个mQueue 是从哪里冒出来的?

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

其实就是在new Handler的时候初始化的,可以看到上面Looper.myLooper();这句话,如果你有心的话其实上面已经有这个方法的实现了:

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

其实就拿的当前线程里面的Looper,因为这个是在主线程里面的new的所以其实就是拿到ActivityThread里面初始化的Looper。 既然已经拿到messageQueue并且将message放到队列里面的那么接下来是怎么出队列进行处理的呢,那就是我们的一开始就说的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);
            end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }


        msg.recycleUnchecked();
    }
}

看上面的代码,我删除了一些东西,最主要的是就拿到messageQueue,并有一个死循环,不断的从messageQueue里面拿去message,接着调用msg.target.dispatchMessage(msg); 在直接发送message的时候,我们看过这个target其实就是绑定的当前的Handler,在dispatchMessage这个方法中就会去调用handMessage这个方法,到这里,整个流程就走完了。

关于死循环的问题,可以看看https://www.zhihu.com/question/34652589/answer/90344494 可以看看这个链接,牵扯到了Linux系统管道的问题了。

通过上面的分析我们也就知道了在主线程中可以newHandler 而在子线程中new Hander 必须自己手动调用Looper的原因了,因为主线程已经帮我实现好了

 

posted on 2018-01-31 16:46  xiaodong135  阅读(120)  评论(0编辑  收藏  举报

导航