Handler消息機制分析

ThreadLocal工做原理

ThreadLocal是一個線程內部的數據存儲類,經過它能夠在指定的線程中存儲數據,數據存儲之後,只有在指定的線程中能夠獲取到存儲的數據,對於其餘線程來講則沒法獲取到數據數組

當某些數據以線程爲做用域而且不一樣線程具體不一樣的數據副本,能夠考慮使用ThreadLocal安全

6.0之前實現bash

set()數據結構

public void set(T value) {
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values == null) {
            values = initializeValues(currentThread);
        }
        values.put(this, value);
    }

首先拿到當前線程localValues,若是localValues沒有值,就會初始化一個Values;
Values中有一個Object[],其實這個數組是一個以key-value的方式來存放threadLocal.set()進來的值;
在數組中key對應的是threadLocal.reference,key的index是經過 threadLocal.hash & threadLocal.mask 計算出來的值
在數組中value對應的就是ThreadLocal.set()進來的值,value的index就是key的index的下一位即index+1

key的index元素說明:reference、hash & mask
reference = new WeakReference<ThreadLocal<T>>(this);this-->ThreadLocal
hash = hashCounter.getAndAdd(0x61c88647 * 2);
    hashCounter = new AtomicInteger(0);原子操做Integer的類,高併發安全
mask = Object[].length-1
複製代碼

get()併發

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);
    }
複製代碼

7.0之後的實現高併發

set()oop

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

與以前不一樣的就是Value實現改成了ThreadLocalMap,將以前的thread.localValues改爲了threadLocals;
首先拿到當前線程threadLocals,若是localValues沒有值,就會初始化一個ThreadLocalMap;
ThreadLocalMap中有一個Entry[],這個Entry是一個以key-value的方式來存放threadLocal.set()進來的值;
Entry繼承自WeakReference<ThreadLocal>
    key存放的就是ThreadLocal,內部仍是存放了一個weakReference,泛型爲ThreadLocal
    value存放的就是set進來的值
entry的index仍是經過 threadLocal.hash & threadLocal.mask 計算出來的
複製代碼

get()post

public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null)
                return (T)e.value;
        }
        return setInitialValue();
    }
複製代碼

MessageQueue 工做原理

  • 經過一個單鏈表的數據結構來維護消息列表,單鏈表插入刪除操做有優點;
  • enqueueMessage()就是往單鏈表中插入數據
  • next()是一個無限循環的方法,若是消息隊列中沒有消息一直阻塞,當有新消息到來時,next方法返回這條消息並將其從單鏈表中移除

Looper 工做原理

  • 不停地從MessageQueue中查看是否有新消息,有的話馬上處理,不然一直阻塞ui

  • 能夠經過Looper.prepare()爲當前線程建立一個looper,經過loop方法開啓消息輪詢this

  • Looper也是能夠退出的,提供了quit()和quitSafely();

    • 兩者區別
    • quit直接退出
    • quitSafely設定了一個退出標記,而後把消息隊列中已有的消息處理完畢退出

    Looper退出之後,handler會發送失敗,send()返回false

  • loop()方法是一個死循環,惟一跳出循環的方式就是MessageQueue.next()方法返回null

  • 當looper的quit方法被調用的時候就會調用MessageQueue的quit或者quitSafely方法通知消息隊列退出,不然loop()方法就會無限循環下去

  • 當沒有消息時,next方法一直阻塞,致使loop方法阻塞

  • 若是next方法返回了消息,Looper就會處理,msg.target.dispatchMessage(msg)

Handler 工做原理

  • Handler發送消息的過程就是向消息隊列中插入了一條消息,MessageQueue的next方法就會返回這條消息給Looper,Looper收到消息就開始處理,最終由Looper將消息交給Handler處理,即Handler的dispatchMessage方法
  • 檢查Message的callBack是否爲null,不爲null就經過handleCallback來處理消息,Message的callBack是一個Runable對象,實際上就是handler的post方法所傳遞的Runnable參數
  • 其次檢查mCallback是否爲null,不爲null就調用mCallback的handleMessage來處理消息,Callback是個接口
  • 最後會執行handler的handleMessage()

爲何主線程不會由於Looper.loop()裏的死循環卡死?

進入死循環以前便建立了新Binder線程,建立一個Binder線程(具體是指ApplicationThread,Binder的服務端,用於接收系統服務AMS發送來的事件),該Binder線程經過Handler將Message發送給主線程

ActivityThread經過ApplicationThread和AMS進行進程間通訊,AMS以進程間通訊的方式完成ActivityThread的請求後回調ApplicationThread中的Binder方法,而後ApplicationThread會向H發送消息,H收到消息後會將ApplicationThread中的邏輯切換到ActivityThread中去執行,即切換到主線程中執行,這個過程就是主線程的消息循環模型

相關文章
相關標籤/搜索