91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

全面理解Handler第一步:理解消息隊列,手寫消息隊列

發布時間:2020-07-16 13:50:28 來源:網絡 閱讀:1404 作者:MDove 欄目:移動開發

前言

Handler機制這個話題,算是爛大街的內容。但是為什么偏偏重拿出來“炒一波冷飯”呢?因為自己發現這“冷飯”好像吃的不是很明白。最近在思考幾個問題,發現以之前對Handler機制的了解是在過于淺顯。什么問題?

  • Handler機制存在的意義是什么?能否用其他方式替換?
  • Looper.loop();是一個死循環,為什么沒有阻塞主線程?用什么樣的方式解決死循環的問題?

如果透徹的了解Handler,以及線程的知識。是肯定不會有這些疑問的,因為以上問題本身就存在問題。

就這倆個小問題,就發現自己在學習道路上的不扎實,所以這段時間重新理解了一下Handler。先預告一小下下,關于Handler的內容將是一個系列文章,今天這一篇內容重點在于Handler的理解,以及對消息隊列的思考。

正文

1、Handler機制為了什么?

我們都知道,在Android開發中,無法在子線程中更新UI。

我們先思考一個問題?為什么不能在子線程更新UI。如果看過View繪制的源碼,我們都知道不能在子線程更新UI的原因是:ViewRootImpl中有這么一個方法:

void checkThread() {
    if (mThread != Thread.currentThread()) {
        throw new CalledFromWrongThreadException(
            "Only the original thread that created a view hierarchy can touch its views.");
    }
}

很明顯這是人為限制的一個操作。那我們在思考,為什么谷歌開發Android系統時要這么限制?

其實不難推測出來。對于線程來說,我們都知道線程與線程之間是內存共享的。所以如果某一時刻多個子線程同時去更新UI,那么對于繪制UI來說便成為了一個不安全的操作。為了保證UI繪制的正確性,此時勢必要增加鎖,以同步的方式去控制這個問題。

然而加鎖的方式顯然是一種犧牲性能的方式。

那么還有沒有其他方案呢?很顯然,最終谷歌選擇了只能在主線程更新UI,應運而生的Handler機制被創造出來了。但是它也不是什么新概念,說白了就是消息隊列。實現原理也很簡單:只允許一個線程(主線程)去更新UI,子線程將消息放到消息隊列中,由主線程去輪詢消息隊列,拿出消息并執行。

這也就是我們的Handler機制。

2、消息隊列

這種單線程 + 消息隊列的模型其實應用很廣。比如在Web前端之中,對于JavaScript來說,被設計時就決定了單線程模型。假設如果 Javascript 被設計為多線程的程序,那么操作 DOM 必然會涉及到資源的競爭。此時只能加鎖,那么在 Client 端中跑這么一門語言的程序,資源消耗和性能都將是不樂觀的。但是如果設計成單線程,并輔以完善的異步隊列來實現,那么運行成本就會比多線程的設計要小很多了。

所以我們可以看到,Handler機制的思路可以說是一個頗為常見的設計。

既然本質是消息隊列,是不是我們自己也可以寫一套消息隊列來感受一下Handler的設計思路呢?沒錯,接下來讓我們一起實現一套簡單的消息隊列:

3、手寫消息隊列

我們先來捋一捋思路:

Looper中創建了MessageQueue,Handler之中又通過ThreadLocal拿到主線程new出來的Looper,因此Handler就持有了MessageQueue,又因此線程間是內存共享的,所以子線程可以通過Handler去往MessageQueue之中發送Message。

Looper.loop()死循環輪詢MessageQueue,拿到Message就回調其對應的方法。

這樣整個Handler機制就運轉起來了。接下來我們就依靠這個思路,實現自己的消息隊列,為了代碼更簡潔,以及和Handler機制產生區別,我這里省略一些操作比如ThreadLocal之類的。

3.1、代碼實現

代碼結束后有解釋


public class MainMQ {
private MyMessageQueue mMQ;
public static void main(String[] args) {
    new MainMQ().fun();
}

public void fun() {
    mMQ = new MyMessageQueue();
    System.out.println("當前線程id:" + Thread.currentThread().getId());
    new Thread(new Runnable() {
        @Override
        public void run() {
            // 省略try-catch
            Thread.sleep(3000);
            mMQ.post(new MyMessage(new Runnable() {
                @Override
                public void run() {
                    System.out.println("執行此條消息的線程id:" + Thread.currentThread().getId());
                }
            }));
        }
    }).start();

    loop();
    System.out.println("死循環了,我永遠也不被執行~");
}

public void loop() {
    while (true) {
        // 省略try-catch
        MyMessage next = mMQ.next();
        if (next == null) {
            continue;
        }
        Runnable runnable = next.getRunnable();
        if (runnable != null) {
            runnable.run();

    }
}

}

這里沒有使用Looper這種思想,因為Looper本質就是使用ThreadLocal創建一個和主線程唯一關聯的Looper實例,并以此保證MessageQueue的唯一性。

知道這個原理之后,這個demo。直接在主線程中new MessageQueue(),是同樣的道理,然后調用loop()方法死循環輪詢MessageQueue中的Message,不為null則執行。

main()方法中start了一個子線程,然后sleep3秒后,往MessageQueue中post Message。效果很簡單,我猜很多小伙伴已經猜到了:

![](https://cache.yisu.com/upload/information/20200311/46/177936.jpg?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=)

> 貼一下MessageQueue和Message
```java
public class MyMessageQueue {
    private final Queue<MyMessage> mQueue = new ArrayDeque<>();

    public void post(MyMessage message) {
        synchronized (this) {
            notify();
            mQueue.add(message);
        }
    }

    public MyMessage next() {
        while (true) {
            synchronized (this) {
                // 省略try-catch
                if (!mQueue.isEmpty()) {
                    return mQueue.poll();
                }
                wait(); 
            }
        }
    }
}
public class MyMessage {
    private Runnable mRunnable;

    public MyMessage(Runnable runnable) {
        mRunnable = runnable;
    }

    public Runnable getRunnable() {
        return mRunnable;
    }
}

3.2、思考存在的問題

細心的小伙伴,可能有留意到loop()方法執行后有這么一行代碼,然后效果圖中并沒有被打印:

System.out.println("死循環了,我永遠也不被執行~");

當然這是必然的,畢竟我們的loop()是一個死循環,后邊的代碼是不可能被執行的。其實我們ActivityThread中調用了Looper.loop()之后,也沒有任何代碼了。

這里可能有小伙伴有疑問了。loop()死循環了,那么我們在主線程中的生命周期回調怎么辦?豈不也不被執行了?其實不然,通過上述的消息隊列,我們就能看出:我們在手寫的這個demo中,loop啟動前start了一個子線程,由子線程發送Message交由loop去執行。保證了消息的流暢性。

那是不是我們Android中的loop也是這種思路?沒錯,main中的loop啟動前,的確會起一個子線程......

不要著急,關于這個問題,讓我們下篇文章再展開~

結尾

今天這篇文章是全面理解Handler機制的第一篇,內容大多并沒有直切到Handler機制本身,而是從外部去思考Handler的設計。而接下來的內容則是對Handler內部源碼進行剖析了。

希望可以對小伙伴們有所幫助,如果感覺有收獲,歡迎點贊,收藏,關注呦~

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

高雄县| 金沙县| 新田县| 晋中市| 电白县| 板桥市| 南华县| 和静县| 黔西县| 新田县| 绥化市| 五河县| 宣武区| 遂溪县| 大悟县| 莆田市| 南皮县| 西平县| 泰宁县| 玛多县| 梅州市| 开阳县| 肥乡县| 昌邑市| 南和县| 盐城市| 田阳县| 宁晋县| 二手房| 临邑县| 合江县| 岐山县| 井陉县| 阿合奇县| 柏乡县| 宝坻区| 大荔县| 都江堰市| 南漳县| 庐江县| 雷州市|