从源码找答案:
随便找一个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并赋值。