很长一段时间以来,面试的时候我总喜欢问一个问题:为什么只能在UI线程对View
进行操作?android程序员在涉足android开发的早期应该就有这样一个认识,但是没有多少人知道究竟是为什么。以至于后来,我也就不愿意问这个问题了,不知道这个问题的答案其实也不妨碍候选人做出好的功能。虽然感觉是很自然的事情,但是不弄清楚总觉得有些不舒服,于是,我花了点时间研究了一下。
其实这个问题的答案并不复杂,只要看一下android源码就能够定位出来。我看的是android2.3的源码,2.3系统确实显得有些过时,但是android很多基本的东西从2.3到4.0都没有发生根本性的变化(5.0没有研究过,不敢妄下断论)。
View的一些基本操作
对View
进行操作无非是使其可见或者不可见,给ViewGroup
加一个View
或移除一个View
,改变View
的大小等,这些操作直接或者间接地会调用到View
的invalidate
和requestLayout
接口,这两个接口的调用是递归式的,最终又会调用到ViewRoot
的invalidateChild
和requestLayout
接口,ViewRoot
又是什么?我们都知道View
其实是一个个树状的结构,你可以认为ViewRoot
就是这些树状结构的根节点。重新回来看一下这两个接口的实现如下:
public void invalidateChild(View child, Rect dirty) {
checkThread();
if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty);
if (mCurScrollY != 0 || mTranslator != null) {
mTempRect.set(dirty);
dirty = mTempRect;
if (mCurScrollY != 0) {
dirty.offset(0, -mCurScrollY);
}
if (mTranslator != null) {
mTranslator.translateRectInAppWindowToScreen(dirty);
}
if (mAttachInfo.mScalingRequired) {
dirty.inset(-1, -1);
}
}
mDirty.union(dirty);
if (!mWillDrawSoon) {
scheduleTraversals();
}
}
public void requestLayout() {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
值得注意的是,这两个接口中都调用到了checkThread
这个接口,那么这个接口又是检查什么呢:
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(