阿里的專家說不能,當我這麼做的時候竟然讓他羞紅了臉

點擊上方“IT爛筆頭”,選擇“置頂公衆號”

第一時間獲取 IT 技術乾貨!

閱讀文本大概需要 6 分鐘。

1

UI 線 程 的 定 義

作爲一名機智的安卓開發者,似乎每個人心裏都有些公認的約定規則。不在非UI線程中更新UI一定是其中之一,你有沒有想過官方爲什麼不建議我們在非UI線程中去更新UI?在回答這個問題之前,我們先簡單瞭解下何爲UI線程?

UI線程也就是我們平時講的主線程,當系統的zygote進程folk出我們的應用進程時,會爲應用進程創建ActivityThread,這個線程就是我們的主線程,一旦這個線程開啓的時候,其中的Looper就一直死循環的在消息隊列中接收消息用來處理UI相關業務。作爲一個初級工程師這些基本能瞭解。

2

Looper 機 制

這裏我藉助網上的一張圖簡單釋義下這個過程:


圖中畫的過程就是我們的Looper機制,這裏就不再說了,想知道細節的同學可以看我另一篇專門寫Looper的文章:Looper賞析
當然如果你之前已經看過我的文章了,對Looper機制瞭如指掌,那麼作爲一個
中級工程師完全合格了。

3

爲 什 麼 UI 不 是 線 程 安 全 的

官方不讓我們在非UI線程更新UI,原因是UI設計的並非線程安全的,多線程訪問的時候必然會出現展示錯亂,那麼有人就會問了,那將UI設計成線程安全的不就行了嗎?

 

OK,那麼我們就假設將UI設計成線程安全的,那麼當有很多線程去更新UI的時候,必然會涉及到上鎖和釋放鎖,這個過程吃開銷。而UI的更新頻率是非常高的,顯然不適合頻繁的上鎖和釋放鎖,所以官方將UI設計成只在一個線程去更新,也不需要線程安全。 

結一下爲什麼UI不設計成線程安全的?

    • UI是頻繁的變

    • UI需要高效的響應操作,不能頻繁加鎖

    • UI組件需要批量同時繪製保證高效

綜上,UI就不宜設計成線程安全的。

 

那麼有人講了,那不對啊,我怎麼記得可以在子線程更新UI呢?你看我是這麼寫的:

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fake);


        fakeText = findViewById(R.id.fake_tips);
        fakeText.setText("主線程");
        new Thread(new Runnable() {
            @Override
            public void run() {
                fakeText.setText("子線程");
                fakeText.setTextColor(Color.BLACK);
            }
        }).start();
    }

然後就能正常看到text變成“子線程”了,也沒有報錯啊。
然後,我們將代碼稍作修改:

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fake);


        fakeText = findViewById(R.id.fake_tips);
        fakeText.setText("主線程");
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    // 線程等待500毫秒
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                fakeText.setText("子線程");
                fakeText.setTextColor(Color.BLACK);
            }
        }).start();
    }

我們讓子線程沉睡了500毫秒以後再更新UI,這時候就報錯了。如果你看過源碼,其實你應該知道,只要你足夠快,在主線程創建頁面根佈局之前去修改UI,就不會報錯。其實這麼做也沒有實際意義,只是從咬文嚼字上說我們確實可以在子線程修改了UI。 

其實還有一個在子線程中更新UI的場景,實際使用的特別多。那就是SurfaceView,像我們的視頻(VideoView)以及很多遊戲都是使用SurfaceView來處理的,因爲這些場景需要非常高的頻率刷新UI,不太適合放到我們主線程的UI一起操作,所以他們就被設計成在自己單獨的線程處理。 

我們將這個簡單化的講一下,更直白一點就是主線程裏面的UI都在同一個window裏面,SurfaceView相當於在這個window裏面挖了個洞,SurfaceView就是顯示在洞的後面也就是window的後面,你可以在洞的上方放一些View,這樣看起來就是SurfaceView上面疊加了一些普通的View。

推薦閱讀:

高工做CPU架構適配的心得體會

聽了他講的泛型,我就明白爲什麼他的工資比我多30萬了!

千萬別告訴別人,這是我從高工那偷聽來的Java方法分派策略

THANDKS

- End -

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章