只要參與過安卓項目開發一兩年的朋友們應該清楚,為了避免UI渲染出現異常安卓框架限制UI操作只能在主線程中進行,如果貿然在子線程做了UI操作結果會怎樣?我們隨便寫下了如下測試代碼。
不出意外的話,代碼執行報錯拋出了名為CalledFromWrongThreadException異常,這件事告訴我們UI操作必須在主線程中進行。
那么是不是在所有情況下,子線程都不能做UI操作呢?換句話說是不是只要在子線程中操作UI就一定會拋出上述異常呢?今天我們不妨針對這個問題做下研究。
還是以上述代碼為例,我們只在布局中把TextView的layout_width改成match_parent的話,業務邏輯什么都不改,再運行一下代碼竟然發現一切運行正常。
怎么樣?這種情況你們想到了嗎?看到這里有沒有顛覆自己的世界觀?也就是說主線程里不能操作UI這句話并不是完全正確,那么什么情況下不正確呢?通過上面的例子來看,至少當TextView組件足夠大以至于能容納其內容時上面這句是不正確的,除非你不承認修改TextView文本是UI操作。
下面我們試圖從源碼(Api 28)角度來分析為什么會出現上面這種打破了安卓程序員固有思維的現象,我們先討論出異常這種情況。從TextView設置文本的方法setText的方法調用棧可知,這個方法會調用View的requestLayout方法,而這個方法最終又會調用ViewRootImpl的checkThread方法,正是在這個方法里拋出了上述異常。
那么我們將TextView的寬度改成足夠大的時候,方法的調用棧又會發生什么變化呢?我們再回到TextView的checkForRelayout方法中,在這個方法的8862行是判斷TextView是否需要根據文本的內容變化來改變控件的大小。如果控件足夠大的話,requestLayout方法將不會調用,所以也不至于去校驗布局操作是在哪個線程處理了。
綜上,子線程是可以處理UI操作的,只要沒涉及到控件布局變化就行,其實也不僅限于TextView,大家有時間完全可以嘗試其他控件如ImageView。
不過雖然在某些情況下可以在子線做UI操作,但是還是不建議那樣做,因為在開發過程中很難去判斷也沒必要去判斷文本內容會不會超出控件,作為開發者還是要遵守安卓開發規范盡量保證在主線程里操作UI。不過如果有一天面試的時候有人問你主線程是否可以做UI操作的話,你應該能舉出上面這種情況,這樣面試官一定會對你刮目相看。
舉報/反饋