今天一位朋友問(wèn)了我一個(gè)問(wèn)題蛉艾,android更新ui的時(shí)候捉超,如果不在主線程更新ui猜拾,系統(tǒng)就會(huì)報(bào)出錯(cuò)誤,應(yīng)用崩潰
CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
基于此,簡(jiǎn)讀了一下View ViewRootImpl 的源碼肺樟。
總得來(lái)說(shuō)android 在UI線程創(chuàng)建UI元素檐春,并把線程的引用存儲(chǔ)到UI控件中,么伯。在更新UI之前檢測(cè)下當(dāng)前線程是否跟創(chuàng)建自己的線程是同一個(gè)線程疟暖。
每個(gè)View持有一個(gè)viewParent的引用。ViewParent 是一個(gè)接口田柔,ViewGroup ViewRootImpl 都實(shí)現(xiàn)了這個(gè)接口俐巴。
protected ViewParent mParent;
public interface ViewParent{
...
public void invalidateChild(View child,Rect r);
public ViewParent invalidateChildInParent(int[]location,Rect r);
...
}
更新View invalidate的時(shí)候,會(huì)最終調(diào)用到ViewGroup的invalidateChild
最終調(diào)用ViewRootImpl的invalidateChidInParent方法
如果View在invalidate之前調(diào)用了requestLayout方法硬爆,那么這個(gè)之后欣舵,viewRootImpl的requestLayout,就會(huì)調(diào)用checkThread來(lái)檢測(cè),此方法就是檢測(cè)創(chuàng)建自己的線程跟更新自己的線程是否是同一個(gè)線程。
如ImageView在setImageResource的時(shí)候缀磕,判斷邻遏,如果新的resource 的width和height跟old都相同,就不會(huì)調(diào)用requestLayout虐骑,這個(gè)時(shí)候會(huì)在invalidate的時(shí)候進(jìn)行檢查。而在setScaleType的時(shí)候赎线,只要跟原scaleType不一致廷没,就會(huì)requestLayout,這個(gè)時(shí)候垂寥,在requestLayout的時(shí)候就會(huì)被檢測(cè)到颠黎。
如TextView在setText的時(shí)候會(huì)調(diào)用checkForRelayout,方法會(huì)調(diào)用requestLayout