Android中的消息機(jī)制

Android 中的消息機(jī)制其實(shí)就是指的是 Handler 消息機(jī)制以及附帶的 LooperMessageQueue 的工作流程。

1.Android 為什么提供Handler?

  • 解決子線程不能訪問 UI 的問題
    ViewRootImpl 中有一個checkThread() 方法:
void checkThread() {
        if (mThread != Thread.currentThread()) {
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
        }
    }

2. 為什么子線程不能訪問UI呢?

  • AndroidUI 控件不是線程安全的,如果在多線程中并發(fā)訪問 UI 控件會導(dǎo)致 UI 控件處于不可預(yù)期的狀態(tài)

3.那為什么不給UI控件加上鎖機(jī)制呢?

  • 加鎖會增加UI邏輯復(fù)雜性
  • 鎖機(jī)制會降低線程訪問UI的效率

4.Handler 的工作原理

5.ThreadLocal

Looper.java

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

ThreadLocal.java

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

threadLocals 屬于 Thread 里的一個變量

ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
public void set(T value) {
        //獲取當(dāng)前的線程對象
        Thread t = Thread.currentThread();
        //getMap(t) 我們就知道了就是獲取當(dāng)前線程里的    
       //threadLocals 變量 ,
      //類型是ThreadLocal.ThreadLocalMap類型
        ThreadLocalMap map = getMap(t);
      //判斷取出的是否為空,第一次應(yīng)該為空
        if (map != null)
            //第二次不為空,將key:ThreadLocal;value:Looper存入
            map.set(this, value);
        else
            //為null時,創(chuàng)建ThreadLocalMap對象,并且將值與線程存入
            createMap(t, value);
    }

那么我們需要知道 ThreadLocalMap 是如何存儲的?

//ThreadLocal 靜態(tài)內(nèi)部類
static class ThreadLocalMap {

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

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

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

        /**
         * The table, resized as necessary.
         * table.length MUST always be a power of two.
         */
        private Entry[] table;

     xxx ....
 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);
        }
    
/**
         * 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;
            //取出那個下標(biāo)
            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();
        }
}

6. 主線程 的 Looper 是何時創(chuàng)建的?

ActivityThreadmain 方法中創(chuàng)建的,一起來看下:

 public static void main(String[] args) {

    Looper.prepareMainLooper();
    xxx....
   Looper.loop();
}

Looper.prepareMainLooper();

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

prepare 方法中又創(chuàng)建threadLocalLooper的關(guān)聯(lián) , 而且加了判斷,說明一個線程只能有一個Looper,而后創(chuàng)建了 Looper 的實(shí)例。
Looper 構(gòu)造方法

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

創(chuàng)建了 MessageQueue 實(shí)例,獲取當(dāng)前的線程。

而在Handler 的創(chuàng)建構(gòu)造函數(shù)中:

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

直接取出當(dāng)前線程的 Looper--mLooperLooper 中的 MessageQueue 對象。后面就是發(fā)消息,消息入隊(duì),Looper取出消息,進(jìn)而調(diào)用 handlerhandleMessage 方法。

看書,隨筆記。如有問題,可指出。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容