Android Handler机制源码细节实现分析

1.Handler机制介绍
Handler机制主要用于线程通信,handler负责把message(消息)发送到MessageQueue(消息队列)里面,每个线程对应一个looper,不停的从消息队列取出消息,然后发送给handler ,交个它去处理消息。一个线程对应一个looper,一个looper对应一个消息队列。
2.Handler线程通信代码

Thread hanMeiMeiThread = new Thread("哈哈") {
    @Override
    public void run() {
    		//初始化looper
        Looper.prepare();
        handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                Log.e("tag","接受到的消息");
            }
        };
        //looper开启循环
        Looper.loop();
    }
};
bt_send.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
    		//发送消息
        handler.sendEmptyMessage(0);
    }
});

3.Handler机制源码分析
handler机制主要是两个重要的方法Looper.prepare()和Looper.loop(),handler.sendEmptyMessage()逐一分析三个方法看着两个方法都做了什么

(1).Looper.prepare()

初始化looper并且把这个Looper关联到他的创建线程里面保证一个线程一个Looper

private static void prepare(boolean quitAllowed) {
    //用 sThreadLocal 存储Looper,当之前有looper会抛出异常,一个线程只能创建一次looper,也就是prepare一次
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}

ThreadLocal从名字来看是线程的本地存储,是把Looper和线程关联起来的

public void set(T value) {
    Thread t = Thread.currentThread();
    //查找Thead里面是否保存了ThreadLocalMap一个Thread关联一个ThreadLocalMap里面根据THreadLocal为key
    //准确来说是ThreadLocal的hash值作为key,Looper作为value存储(这个在这里看不到得看ThreadLocalMap里面的代码,ThreadLocal作为一个中转,我认为是主要为了生成key的作用保存到ThreadLocalMap)
    ThreadLocalMap map = getMap(t);
    if (map != null)
        //之前THread关联过ThreadLocalMap,给Thread里面的ThreadLocalMap里面根据ThreadLocal 为可以         				//looper 为value 更新LocalMap
        map.set(this, value);
    else
        //创建一个ThreadLocalMap
        createMap(t, value);
}

先根据我整体的总结,理解一下每个方法都干了什么,方便后来代码的分析,一个方法一个方法进行分析

①获取Thread 关联的ThradLocalMap

ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

其实这个方法是第一次ThreadLocal.set方法的时候执行的是createMap()初始化ThreadMap,接着继续看ThreadLocalmap构造函数里面的操作,t.threadLocals在Thread里面的exit()方法才会置空,这个方法不是在Thrad 挂掉的时候执行,所以getMap(Thread t)很多情况都能获取t.threadLocals

 //初始化一个Entry的数组这个数组是循环数组,根据ThreadLocal生成hash值作为key
        //ThreadLocal是根据threadLocalHashCode 这是个final修饰的一个对象只有一个这个值,Looper对应					//ThradLocal是个static的,这样来说所有的Thread 里面存储的Looper 在ThreadLocalmap 里面的						//entry里面的位置是一样的
        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);
        }

③ map.set(this, value);这个方法是当前的Thread更新ThreadLocal为key,looper为value

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);
    //数组Entry 以弱引用的形式存储ThreadLocal,
    //通过THradLocal 生成index 索引到数组的位置,取出对应entry,能找见entry
    for (Entry e = tab[i];
         e != null;
         e = tab[i = nextIndex(i, len)]) {
        ThreadLocal<?> k = e.get();
        //entry里面的的ThreadLocalMap没有被java垃圾回收机制回收,直接赋值
        if (k == key) {
            e.value = value;
            return;
        }
        //过期的数据,就会清空一下废弃的数据,然后重新hash一下这个,这里面算法比较复杂我稍微看了一下没有深究
        if (k == null) {
            replaceStaleEntry(key, value, i);
            return;
        }
        //如果当前索引到的位置没有找见对应的entry 既没有被回收,也不是当前的ThreadLocal对应的key 这个时					//候是hash值冲突,使用线性探测的方法继续索引
    }
    //如果对应index数据为空,添加一个ThreadLoca 和Looper绑定的entry放到数组里面,数组的拥有数据的size超		//过总size的2/3扩充一下数组,重新hash一下
    tab[i] = new Entry(key, value);
    int sz = ++size;
    if (!cleanSomeSlots(i, sz) && sz >= threshold)
        rehash();
}

(2).Looper.loop();

//取出looper
final Looper me = myLooper();
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

    try {
        //发给handler进行处理
        msg.target.dispatchMessage(msg);
        end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
    } finally {
        if (traceTag != 0) {
            Trace.traceEnd(traceTag);
        }
    }

}

总结Looper .loop()的作用就是不停的从消息队列中取出消息(queue.next()取出消息的方法)然后发给handler 进行处理,msg.targe就是你发送消息的hander,最后回到了handler进行处理,下面对myLooper()和queue.next()做一下详细的分析
①myLooper()

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

也是从ThreadLocal里面获取的

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    //若果没有ThreadLocalMap 这里创建一个Looper为空的ThreadLocalMap绑定到Thread里面
    return setInitialValue();
}

从Thread里面获取依赖的ThreadLocalMap,从ThreadLocamap里面取出对应的entry,这段代码,在之前的部分详细说明过,这里就不再说了,分析一下map.getEntry

private Entry getEntry(ThreadLocal<?> key) {
    int i = key.threadLocalHashCode & (table.length - 1);
    Entry e = table[i];
    //没有哈希冲突的情况
    if (e != null && e.get() == key)
        return e;
    else
    		//有哈希冲突的情况
        return getEntryAfterMiss(key, i, e);
}

起立里面的代码逻辑很简单,通过ThreadLocal 的hash值生成,得到index,如果对应的Entry存储的Key 和当前的THreadLocal 相同,证明没有哈希冲突,取出值return,有冲突,在哈希一下,去下数组的下一个index Entry ,比较其中的Key直至找见对应的Looper,大家有兴趣可以详细看一下getEntryAfterMiss这个方法,我在这就不过多分析了。
②queue.next()

//死循环,取出队列的头部消息
for (;;) {
    if (nextPollTimeoutMillis != 0) {
        Binder.flushPendingCommands();
    }

    nativePollOnce(ptr, nextPollTimeoutMillis);

    synchronized (this) {
        // Try to retrieve the next message.  Return if found.
        final long now = SystemClock.uptimeMillis();
        Message prevMsg = null;
        //取出消息队列的队列头部的消息
        Message msg = mMessages;
        if (msg != null && msg.target == null) {
            // Stalled by a barrier.  Find the next asynchronous message in the queue.
            do {
                prevMsg = msg;
                msg = msg.next;
            } while (msg != null && !msg.isAsynchronous());
        }
        if (msg != null) {
            //判断一下消息是否到了延迟的时间,没到的话计算出时间,继续死循环,然后执行 nativePollOnce(ptr, nextPollTimeoutMillis);
            //这个个本地方法通过他堵塞的,然后等时间到了,继续判断消息
            if (now < msg.when) {
                // Next message is not ready.  Set a timeout to wake up when it is ready.
                nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
            } else {
                //到了把消息返回
                // Got a message.
                mBlocked = false;
                if (prevMsg != null) {
                    prevMsg.next = msg.next;
                } else {
                    mMessages = msg.next;
                }
                msg.next = null;
                if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                msg.markInUse();
                return msg;
            }
        } else {
            // No more messages.
            nextPollTimeoutMillis = -1;
        }


    }

(3)handler.sendEmptyMessage(0);发送消息的过程

public final boolean sendEmptyMessage(int what)
{
    return sendEmptyMessageDelayed(what, 0);
}

public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
		/从缓存池里面获取缓存的消息
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageDelayed(msg, delayMillis);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    //消息延迟发送,是通过获取当前开机启动的时间+延迟的时间形成后面的When然后做消息处理的
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
		//这里就看到handler 关联到消息里面了,接受消息的时候就能找见target了
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}
boolean enqueueMessage(Message msg, long when) {
    //when 消息执行的时间要延迟多久
    //
    synchronized (this) {
        if (mQuitting) {
            IllegalStateException e = new IllegalStateException(
                    msg.target + " sending message to a Handler on a dead thread");
            Log.w(TAG, e.getMessage(), e);
            msg.recycle();
            return false;
        }

        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        //判断当前的是不是进入的消息是不是队列头部消息,是的话needWake 为true执行nativeWake()方法
        //nativeWake()的本地调用,然后进一步调用Looper#wake()以唤醒Looper。当native的消息处理完成后,
        // 会导致Java层的nativePollOnce()的调用返回,从而让Java层处理消息。MessageQueue.next()取出消息
        if (p == null || when == 0 || when < p.when) {
            // New head, wake up the event queue if blocked.
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else {
            //通过when判断新进来的message要放进MessageQue的位置
            // Inserted within the middle of the queue.  Usually we don't have to wake
            // up the event queue unless there is a barrier at the head of the queue
            // and the message is the earliest asynchronous message in the queue.
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            for (;;) {
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            msg.next = p; // invariant: p == prev.next
            prev.next = msg;
        }

        // We can assume mPtr != 0 because mQuitting is false.
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

Message.obtain()方法分析从全局池返回新的消息实例。允许我们在许多情况下避免分配新对象。

//从消息池队列取出缓存的消息,一个长度为50的缓存的消息队列
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();
}
//创建缓存的池子的消息队列,在looper.looper的时候执行的
void recycleUnchecked() {
    // Mark the message as in use while it remains in the recycled object pool.
    data = null;

    synchronized (sPoolSync) {
        if (sPoolSize < MAX_POOL_SIZE) {
            next = sPool;
            sPool = this;
            sPoolSize++;
        }
    }
}

4.总结
Thread通过ThradLocamap 通过ThradLocal 为key,Looper为value的方式,Looper 为value存到ThreadLocalMap的Entry里面,因为一个Looper只有一个THreadLocal,都是通过这个ThreadLocal存储的Looper,这样保证了一个Thread 拥有一个Looper,Looper.loop()类似一个永动机,不停的从消息队列的队列的头部取出消息,判断延迟的时间是否到了,不到的话睡眠堵塞一段时间,继续取消息,取出来的消息交给handeler进行处理,handler发消息,这个新建的消息会把当前的handler绑定到message的target里面,新建的消息默认是消息池里面的缓存的消息,也是以队列的形式存在,最大长度是50。然后是把消息放到消息队列的合适位置,消息队列是以个链表的形式存储数据的先进先出,如果消息的位置是消息头的话,会唤醒消息队列执行next方法继续取消息,返回给handler 进行处理。

猜你喜欢

转载自blog.csdn.net/u010939317/article/details/106159839