一。討論android非主線程是否能更新UI呢?
我們?cè)陂_發(fā)App時(shí),經(jīng)常會(huì)遇到這樣的Log崩潰日志
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views
很簡(jiǎn)單的意思:只有創(chuàng)建該view的線程才能操作給view。關(guān)于這個(gè)問題我先借用別人提出的案例,
案例一:
案例二:
運(yùn)行以上代碼出現(xiàn)的結(jié)果是:案例一可以正常運(yùn)行,案例二出現(xiàn)崩潰現(xiàn)象,其錯(cuò)誤原因均是文章開頭提出的異常,對(duì)于這個(gè)結(jié)果讓前期的我非常困惑,以下我會(huì)分析原因:
(1)崩潰的原因當(dāng)然是我在非UI線程下調(diào)用了setText方法,我依次對(duì)setText往下遍歷
Textview.setText->TextView.checkForRelayout->invalidate->ViewGroup.invalidateChild->
發(fā)現(xiàn)導(dǎo)致崩潰的最根本原因是ViewRootImpl.checkThread()方法。
void checkThread() {
if?(mThread?!=?Thread.currentThread())?{
throw?new?CalledFromWrongThreadException(
"Only?the?original?thread?that?created?a?view?hierarchy?can?touch?its?views.");
}
}
(2)那為什么案例一可以正常運(yùn)行呢?我們先回顧一下Activity與Fragment生命周期探討?,activity是onCreate中進(jìn)行界面的數(shù)據(jù)準(zhǔn)備,onStart()之后,Activity的界面就對(duì)用戶可見了。案例一線程開啟是在oncreat()且子線程并沒有處理復(fù)雜的邏輯,所以導(dǎo)致線程開啟后setText方法還會(huì)是在onCreat生命周期內(nèi)、案例二很明顯setText是在onResume()生命周期內(nèi)。
分析:不同生命周期內(nèi)調(diào)用setText()方法其實(shí)本質(zhì)不同,
1.onCreat生命周期內(nèi)調(diào)用setText,界面不可見,所以不會(huì)調(diào)用invalidate方法,那就更不會(huì)調(diào)用checkThread,所以不會(huì)崩潰,其實(shí)此時(shí)的setText方法的含義并沒有實(shí)現(xiàn)更新view操作。
2.onResume生命周期內(nèi),界面已經(jīng)可見,任何的更新view的操作,勢(shì)必都會(huì)導(dǎo)致調(diào)用invalidate
來更新view,所以此時(shí)在非UI線程下更新view必會(huì)崩潰
綜上所述得出結(jié)論:非UI線程下不能更新view
二。為什么google設(shè)計(jì)非UI線程下不能更新view?
如果可以并發(fā)的更新UI,事實(shí)上是 “is not thread safe”的,也就是線程不安全。我們都知道,線程安全問題其實(shí)就是,不同的線程對(duì)同一塊資源的調(diào)用。在更新UI的同時(shí),會(huì)涉及context資源的調(diào)用,所以產(chǎn)生了線程安全問題。
你足夠了解Context嗎?
三。如何在非主線程下更新View操作?
1.方法一:Handler
2.方法二:用Activity對(duì)象的runOnUiThread方法更新
3.方法三:View.post(Runnable r)
這時(shí)候你要注意在onPause的時(shí)候view.removeCallbacks(runable),取消掉這個(gè)線程。