從零實(shí)現(xiàn)ImageLoader(四)—— Handler的內(nèi)心獨(dú)白

目錄

從零實(shí)現(xiàn)ImageLoader(一)—— 架構(gòu)
從零實(shí)現(xiàn)ImageLoader(二)—— 基本實(shí)現(xiàn)
從零實(shí)現(xiàn)ImageLoader(三)—— 線程池詳解
從零實(shí)現(xiàn)ImageLoader(四)—— Handler的內(nèi)心獨(dú)白
從零實(shí)現(xiàn)ImageLoader(五)—— 內(nèi)存緩存LruCache
從零實(shí)現(xiàn)ImageLoader(六)—— 磁盤緩存DiskLruCache

前情回顧

在上一篇文章里,我們實(shí)現(xiàn)了ImageLoader的異步加載功能,探索了線程池的原理,不過卻遺留了一個(gè)問題,也就是這一句代碼:

ImageLoader.HANDLER.post(() -> imageView.setImageBitmap(image));

上一篇文章里只是簡單的提了一下這句代碼將imageView.setImageBitmap(image)切換到了主線程執(zhí)行,可這是怎么做到的呢?要知道,用戶可以在主線程使用我們的ImageLoader,同樣也可以在子線程使用,我們甚至都不知道自己處于什么線程。

而這句代碼可以成功的關(guān)鍵就在于HANDLER的初始化:

public class ImageLoader {
    static final Handler HANDLER = new Handler(Looper.getMainLooper());
}

看到這句代碼,有的同學(xué)可能已經(jīng)有了答案,也有同學(xué)可能依然一頭霧水,不管你看沒看懂,今天的這篇文章一定會讓你對Handler的運(yùn)行機(jī)制有一個(gè)更加清晰的理解。

原理

Handler這個(gè)我們平時(shí)開發(fā)時(shí)常見的老朋友,他的作用應(yīng)該已經(jīng)不必多說了,大多數(shù)的同學(xué)應(yīng)該也對MessageQueue、LooperMessage這幾個(gè)類有所了解,可他們之間是怎么協(xié)同工作的呢?讓我們先來看一張圖:

Handler原理圖

很明顯,這是一個(gè)典型的生產(chǎn)者-消費(fèi)者模型,Handler通過sendMessage()方法將消息放入阻塞隊(duì)列MessageQueue中,而Looper.loop()方法則會一直檢測MessageQueue中是否有可用的消息,得到消息后,Looper就會調(diào)用Handler.dispatchMessage(),進(jìn)而通過handleMessage()處理消息。

需要注意的是,這只是一個(gè)線程里的結(jié)構(gòu),如果是多線程的話,每個(gè)使用Handler線程都應(yīng)該有一個(gè)這樣的結(jié)構(gòu),所以準(zhǔn)確一點(diǎn)的圖應(yīng)該是這樣:

準(zhǔn)確一點(diǎn)的圖

那有人就要問了,按照上面的說法,消息在同一個(gè)線程里轉(zhuǎn)來轉(zhuǎn)去有什么意義呢?說好的線程間通信呢?

Java里的堆內(nèi)存是線程間共享的,所以理論上來說在一個(gè)線程里可以拿到另一個(gè)線程的任意對象(其實(shí)對象都在一個(gè)地方,也就不分哪個(gè)線程了,這么說只是為了方便理解),我們這里需要的只是Handler,他就是開啟線程間通信大門的鑰匙,拿到了哪個(gè)線程的Handler也就可以向哪個(gè)線程發(fā)送消息。而我們平時(shí)也就是這么使用的:

public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        new Thread(() -> {
            //在子線程中使用主線程的Handler向主線程發(fā)送消息
            mHandler.sendMessage(Message.obtain());
        }).start();
    }

    private Handler mHandler = new MyHandler();

    static class MyHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            doSomething();
        }
    }
}

Looper的線程獨(dú)立性

明白了Handler的工作原理,我們再來學(xué)習(xí)源碼加深一下印象。

要想明白Handler是怎么實(shí)現(xiàn)的,就得先知道Looper是怎么做到線程獨(dú)立的。

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

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

可以看到這里使用ThreadLocal實(shí)現(xiàn)了每個(gè)線程都擁有獨(dú)立的Looper,而MessageQueue作為Looper的成員變量也同時(shí)做到了線程的獨(dú)立。

Handler與Looper的聯(lián)系

Handler又是如何和Looper聯(lián)系到一起的呢?這就要看Handler的構(gòu)造方法了:

public Handler(Callback callback, boolean async) {
    ...

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

public Handler(Looper looper, Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

就在這里Handler確認(rèn)了自己的歸屬,默認(rèn)當(dāng)然是屬于創(chuàng)建自己的線程,而通過Looper參數(shù)手動指定歸屬線程也未嘗不可,這也就是我們文章一開頭所做的。

消息入列

Handler所有的sendMessage()方法最終都調(diào)用了enqueueMessage()

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

很明顯Handler通過queue.enqueueMessage()方法將消息放入了消息隊(duì)列。

消息出列

消息的出列自然是在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(); // 由于MessageQueue是阻塞隊(duì)列,這里有可能阻塞住
        if (msg == null) {
            // 如果msg為空證明消息隊(duì)列已經(jīng)退出了
            return;
        }

        ...

        msg.target.dispatchMessage(msg);

        ...
    }
}

loop()中的代碼看起來很多實(shí)際上大多數(shù)都是一些log代碼,而刪去log代碼剩下的這些相信已經(jīng)一目了然了,loop()中的for循環(huán)會一直嘗試從消息隊(duì)列中取出Message,之后根據(jù)Message.target調(diào)用Handler.dispatchMessage()方法。而dispatchMessage()又會調(diào)用Message.handleMessage(),最終完成消息的處理。

時(shí)序圖

最后我們讓以一個(gè)時(shí)序圖來結(jié)束今天Handler的原理探索:

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

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

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