從源碼找答案:
隨便找一個UI控件的更新操作,例如TextView的 setText 操作然後追蹤:
TextView 類中:
private void setText(CharSequence text, BufferType type,
boolean notifyBefore, int oldlen) {
...
if (mLayout != null) {
checkForRelayout();//調用
}
...
}
/**
* 檢查全新的文本是否需要新的視圖佈局
* 或者只是一個新的文本佈局
*/
private void checkForRelayout() {
//無論如何都會執行下面兩行代碼
...
requestLayout();//重新請求佈局
invalidate();//重繪
...
}
最後,invalidate()調用的是View中的invalidate方法
View 類中:
public void invalidate() {// 1
invalidate(true);
}
public void invalidate(boolean invalidateCache) {// 2
invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
}
void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
boolean fullInvalidate) {// 3
...
final ViewParent p = mParent;
if (p != null && ai != null && l < r && t < b) {
p.invalidateChild(this, damage);
}
//調用ViewParent 接口的 invalidateChild方法,該接口被ViewRootImpl實現,
//並且最終會調用ViewRootImpl的 invalidateChild方法
//直接去看ViewRootImpl的 invalidateChild方法
...
}
記住這個 if 條件,條件中 ViewParent 被 ViewRootImpl 實現。並且做了一個判斷它不爲空程序纔會進行下去,因爲 mParent 在 Activity 的 onResume 方法中被賦值。所以在onCreate時它是空的。這就是爲什麼在onCreate 中子線程可以刷新UI的原因。
ViewRootImpl 類中:
public void invalidateChild(View child, Rect dirty) { // 1
invalidateChildInParent(null, dirty);
}
public ViewParent invalidateChildInParent(int[] location, Rect dirty) { // 2
checkThread();
...
}
void checkThread() { // 3 在這裏 判斷並拋出異常
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
對,沒錯,最後的最後,就是在這裏 判斷並拋出異常的。因爲在View中對 ViewParent 實現類有一個判斷,當它不爲空時纔會執行到這裏,而 ViewParent 在Activity生命週期的 OnResume 方法中才會被賦值。所以在 onCreate 方法中如果使用子線程是可以做一波更新UI操作的。線程在CPU中調度隨機的關係,子線程到這個判斷的時候,主線程可能並沒有創建ViewParent並賦值。