Android 關(guān)于子線程更新UI的那些事

相信大家都有聽過,子線程更新UI的操作。但這種說法,不是很明確。有些人說子線程更新UI會掛,而有些人說子線程可以更新UI。接下來分析下這兩種情況。

先來說說子線程更新UI會掛的問題吧。

在Activity中onCreate完后,會生成一個(gè)ViewRootImpl,View的繪制都是同個(gè)它來實(shí)現(xiàn)的,而ViewRootImpl調(diào)用到requestLayout()來完成View的繪制操作??聪略创a:

//ViewRootImpl.java
 @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }

但布局繪制或者發(fā)生變化時(shí),會調(diào)用requestLayout(),而里面有checkThread(),來看下它的源碼:

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

其中mThread是在ViewRootImpl創(chuàng)建時(shí)初始化的,把創(chuàng)建時(shí)的線程和mThread綁定,而ViewRootImpl又是在主線程初始化的,所以mThread表示主線程。假如更新UI,會調(diào)用requestLayout() -> checkThread() -> mThread != Thread.currentThread(),判斷是否在主線程,如果在子線程中,會拋異常,所以子線程更新UI才會掛。

子線程更新UI會掛的思路明確了,再來看看子線程更新UI為什么不會掛吧。

剛才講到了,在Activity中onCreate完后,會生成一個(gè)ViewRootImpl,那么之后它就會去檢查你更新UI時(shí)在哪個(gè)線程。那假如我在onCreate時(shí)去開一個(gè)子線程更新UI,此時(shí)ViewRootImpl還沒創(chuàng)建,就不會去檢測UI變化,所以在onCreate中子線程是可以更新UI的。

那又有些人說,我不在onCreate里面更新,我就在onCreate后在子線程里面去更新,它還是不會掛,這又是為什么呢?主要還是看下更新UI的方法吧

    text1.setOnClickListener {
        thread {
            it.post {
                text1.text = "change"
            }
        }
    }

這里用一個(gè)點(diǎn)擊事件,開子線程更新UI,運(yùn)行后發(fā)現(xiàn)沒掛,這是為什么?看下post的源碼:

    //View.java
    public boolean post(Runnable action) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            return attachInfo.mHandler.post(action);
        }

通過源碼分析,你就知道這個(gè)mHandler是在ViewRootImpl里面賦值的,mHandler是主線程的Handler,而又掉了handler.post(),所以只要Handler在主線程,那么它post的所有的UI操作都是主線程。看起來像在子線程,實(shí)際是回到主線程更新UI。

繼續(xù)看這種

class MyTestActivity:AppCompatActivity() {
    private val handler = Handler()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.ab)
    }

    override fun onResume() {
        super.onResume()
        getHandler()
    }
    private fun getHandler(){
        thread {
            handler.post {
                text0.text = "change"
            }
        }
    }
}

由于創(chuàng)建handler時(shí)是在主線程,所以這個(gè)handler是屬于主線程的,所以其他的步驟就更上面的一樣了。另一種handler的發(fā)消息更新的方式我就不寫出來了,只要通過handler更新UI的,只要handler是主線程的,必定不會掛。之前有寫過Handler使用的文章,有興趣的同學(xué)可以看看~

還有一種就是調(diào)用runOnUiThread{}

class MyTestActivity:AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.ab)
    }

    override fun onResume() {
        super.onResume()
        thread()
    }
    private fun thread(){
        thread {
            runOnUiThread {
                text.text = "change"
            }
        }
    }
}

顧名思義,在主線程運(yùn)行,只要調(diào)了這個(gè)方法,所有操作都在主線程里面,所有只要的操作不會掛。

總結(jié)一點(diǎn),并不是說子線程可以更新,仔細(xì)點(diǎn)說:子線程中可以在調(diào)用主線程的Handler去更新UI,或者子線程可以調(diào)用runOnUIThread{}切換到主線程更新UI。

之前還遇到一種特殊情況,在onResume中更新UI不會掛,要是在最后一行加上 view.requestLayout()

 @Override
    protected void onResume() {
        super.onResume();
        new Thread() {
            @Override
            public void run() {
                super.run();
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                TextView view = findView(R.id.tv_content);
                view.setText("我在子線程更新");
                view.setBackgroundColor(Color.RED);
                view.requestLayout();
            }
        }.start();
    }

再試一下,就崩了

總結(jié)一點(diǎn):實(shí)際上,就是只要你改view,不觸發(fā)checkThread()就沒事,而TextView的寬高不改變,也不會去觸發(fā)requestLayout(),修改背景也同樣。不會觸發(fā)view的位置大小改變。當(dāng)然,這種情況,不是每個(gè)版本的android都有用,還是要規(guī)范的去主線程更新UI。

最后編輯于
?著作權(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)容