Handler、Looper陵且、MessageQueue源碼解析

首先打開Looper類裁僧,可以看到頭部的說明中有一個小例子,如下:

class LooperThread extends Thread {
    public Handler mHandler;

    public void run() {
        Looper.prepare();

        mHandler = new Handler() {
            public void handleMessage(Message msg) {
                // process incoming messages here
            }
        };

        Looper.loop();
      }
}

這是一個在子線程中創(chuàng)建Handler的例子慕购,我們就以這個例子來講解聊疲。

Looper.prepare();

在子線程中,在創(chuàng)建Handler之前先執(zhí)行了Looper.prepare()沪悲,我們先來看下Looper.prepare()的代碼:

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

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");
    }
    //創(chuàng)建Looper對象获洲,并把Looper存入ThreadLocalMap
    sThreadLocal.set(new Looper(quitAllowed));
}

//ThreadLocal的set()方法
public void set(T value) {
    //獲取當(dāng)前線程
    Thread t = Thread.currentThread();
    //獲取當(dāng)前線程的ThreadLocalMap
    ThreadLocalMap map = getMap(t);
    //把ThreadLocal為key,Looper為value存入ThreadLocalMap
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

//Looper的構(gòu)造方法殿如,創(chuàng)建了一個MessageQueue
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

Looper.prepare()的作用就是創(chuàng)建一個Looper和一個MessageQueue對象贡珊,Looper.prepare()只能調(diào)用一次,再次調(diào)用就會拋throw new RuntimeException("Only one Looper may be created per thread")異常涉馁,所以每一個線程對應(yīng)唯一的一個Looper和一個MessageQueue门岔。

new Handler()

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

    //獲取當(dāng)前線程的Looper對象(Looper.myLooper()的源碼在下面)
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    //獲取當(dāng)前線程的Looper對應(yīng)的MessageQueue
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

從上面可以看出Handler的構(gòu)造方法,其實就是獲取當(dāng)前線程的Looper和MessageQueue烤送,我們通過Handler的sendMessage發(fā)送Message寒随,都是把Message放到MessageQueue中。

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) {
    //把當(dāng)前Handler對象賦值給msg.target
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    //把Message放到MessageQueue中
    return queue.enqueueMessage(msg, uptimeMillis);
}

Looper.myLooper()

Looper.myLooper()先獲取當(dāng)前線程帮坚,再獲取當(dāng)前線程的ThreadLocalMap妻往,然后從ThreadLocalMap中獲取先前存入的Looper。

public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}
//ThreadLocal的get()方法
public T get() {
    //獲取當(dāng)前線程
    Thread t = Thread.currentThread();
    //獲取當(dāng)前線程的ThreadLocalMap
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        //以ThreadLocal為key從ThreadLocalMap取出保存的Looper
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null)
            return (T)e.value;
    }
    return setInitialValue();
}

Looper.loop()

循環(huán)從MessageQueue中取出Message试和,通過msg.target就是發(fā)送Message的Handler讯泣,調(diào)用Handler的dispatchMessage(msg)分發(fā)消息。

public static void loop() {
    //獲取當(dāng)前線程的Looper對象
    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;

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

    //循環(huán)從MessageQueue中取出Message
    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
        final Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }

        final long traceTag = me.mTraceTag;
        if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
            Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
        }
        try {
            //msg.target就是發(fā)送Message的Handler灰署,dispatchMessage(msg)分發(fā)消息
            msg.target.dispatchMessage(msg);
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }

        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }

        // Make sure that during the course of dispatching the
        // identity of the thread wasn't corrupted.
        final long newIdent = Binder.clearCallingIdentity();
        if (ident != newIdent) {
            Log.wtf(TAG, "Thread identity changed from 0x"
                    + Long.toHexString(ident) + " to 0x"
                    + Long.toHexString(newIdent) + " while dispatching to "
                    + msg.target.getClass().getName() + " "
                    + msg.callback + " what=" + msg.what);
        }

        msg.recycleUnchecked();
    }
}

msg.target.dispatchMessage(msg)

Handler的dispatchMessage(msg)把Message交給對應(yīng)的方法處理判帮,這里是handleMessage(msg)局嘁。

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

public void handleMessage(Message msg) {
}

總結(jié)

Looper的prepare()方法創(chuàng)建了一個Looper對象,并且保證每個線程最多只有一個Looper對象晦墙。在主線程中悦昵,系統(tǒng)已經(jīng)初始化了一個Looper對象,因此可以直接創(chuàng)建Handler晌畅,在子線程必須調(diào)用Looper.prepare()創(chuàng)建一個Looper對象但指,并調(diào)用Looper.loop()啟動它。

Handler的構(gòu)造方法抗楔,會獲取當(dāng)前線程的的Looper棋凳,進而獲得關(guān)聯(lián)的MessageQueue,Handler的sendMessage會給msg.target賦值為自身连躏,然后把Message存入MessageQueue剩岳。

Looper不斷的從MessageQueue中取出Message,通過msg.target獲取對應(yīng)的Handler入热,調(diào)用Handler的dispatchMessage(msg)把Message交給對應(yīng)的方法處理拍棕。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市勺良,隨后出現(xiàn)的幾起案子绰播,更是在濱河造成了極大的恐慌,老刑警劉巖尚困,帶你破解...
    沈念sama閱讀 221,273評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蠢箩,死亡現(xiàn)場離奇詭異,居然都是意外死亡事甜,警方通過查閱死者的電腦和手機谬泌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評論 3 398
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來逻谦,“玉大人呵萨,你說我怎么就攤上這事】缈纾” “怎么了?”我有些...
    開封第一講書人閱讀 167,709評論 0 360
  • 文/不壞的土叔 我叫張陵囱皿,是天一觀的道長勇婴。 經(jīng)常有香客問我,道長嘱腥,這世上最難降的妖魔是什么耕渴? 我笑而不...
    開封第一講書人閱讀 59,520評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮齿兔,結(jié)果婚禮上橱脸,老公的妹妹穿的比我還像新娘础米。我一直安慰自己,他們只是感情好添诉,可當(dāng)我...
    茶點故事閱讀 68,515評論 6 397
  • 文/花漫 我一把揭開白布屁桑。 她就那樣靜靜地躺著,像睡著了一般栏赴。 火紅的嫁衣襯著肌膚如雪蘑斧。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,158評論 1 308
  • 那天须眷,我揣著相機與錄音竖瘾,去河邊找鬼。 笑死花颗,一個胖子當(dāng)著我的面吹牛捕传,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播扩劝,決...
    沈念sama閱讀 40,755評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼庸论,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了今野?” 一聲冷哼從身側(cè)響起葡公,我...
    開封第一講書人閱讀 39,660評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎条霜,沒想到半個月后催什,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,203評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡宰睡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,287評論 3 340
  • 正文 我和宋清朗相戀三年蒲凶,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拆内。...
    茶點故事閱讀 40,427評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡旋圆,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出麸恍,到底是詐尸還是另有隱情灵巧,我是刑警寧澤,帶...
    沈念sama閱讀 36,122評論 5 349
  • 正文 年R本政府宣布抹沪,位于F島的核電站刻肄,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏融欧。R本人自食惡果不足惜敏弃,卻給世界環(huán)境...
    茶點故事閱讀 41,801評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望噪馏。 院中可真熱鬧麦到,春花似錦绿饵、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至步清,卻和暖如春要门,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背廓啊。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評論 1 272
  • 我被黑心中介騙來泰國打工欢搜, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人谴轮。 一個月前我還...
    沈念sama閱讀 48,808評論 3 376
  • 正文 我出身青樓炒瘟,卻偏偏與公主長得像,于是被迫代替她去往敵國和親第步。 傳聞我的和親對象是個殘疾皇子疮装,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,440評論 2 359

推薦閱讀更多精彩內(nèi)容