开发过程中在子线程访问控件的时候会出现Only the original thread that created a view hierarchy can touch its views。那么异常是从哪里来的呢?
通过查询源码可见:
class ViewRootImpl{
public ViewRootImpl(@UiContext Context context, Display display, IWindowSession session,boolean useSfChoreographer) {
mContext = context;
mWindowSession = session;
mDisplay = display;
mBasePackageName = context.getBasePackageName();
mThread = Thread.currentThread();
}
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
}
可见ViewRootImpl创建的时候保存了线程信息,checkThread检查了线程信息。那么,哪些方法调用了checkThread呢?
@Override
public void requestFitSystemWindows() {//设置试图不覆盖系统窗口的时候
checkThread();
mApplyInsetsRequested = true;
scheduleTraversals();
}
@Override
public void requestLayout() {//刷新布局的时候
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
@Override
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
checkThread();
if (DEBUG_DRAW) Log.v(mTag, "Invalidate child: " + dirty);
}
void setWindowStopped(boolean stopped) {
checkThread();
if (mStopped != stopped) {
mStopped = stopped;
}
}
@Override
public void requestTransparentRegion(View child) {
// the test below should not fail unless someone is messing with us
checkThread();
if (mView != child) {
return;
}
}
@Override
public void requestChildFocus(View child, View focused) {
if (DEBUG_INPUT_RESIZE) {
Log.v(mTag, "Request child focus: focus now " + focused);
}
checkThread();
scheduleTraversals();
}
@Override
public void clearChildFocus(View child) {
if (DEBUG_INPUT_RESIZE) {
Log.v(mTag, "Clearing child focus");
}
checkThread();
scheduleTraversals();
}
@Override
public void focusableViewAvailable(View v) {
checkThread();
if (mView != null) {
if (!mView.hasFocus()) {
}
}
}
@Override
public void recomputeViewAttributes(View child) {
checkThread();
if (mView == child) {
mAttachInfo.mRecomputeGlobalAttributes = true;
if (!mWillDrawSoon) {
scheduleTraversals();
}
}
}
@Override
public void playSoundEffect(@SoundEffectConstants.SoundEffect int effectId) {
checkThread();
try {
final AudioManager audioManager = getAudioManager();
if (mFastScrollSoundEffectsEnabled
@Override
public View focusSearch(View focused, int direction) {
checkThread();
if (!(mView instanceof ViewGroup)) {
return null;
}
return FocusFinder.getInstance().findNextFocus((ViewGroup) mView, focused, direction);
}
@Override
public View keyboardNavigationClusterSearch(View currentCluster,
@FocusDirection int direction) {
checkThread();
return FocusFinder.getInstance().findNextKeyboardNavigationCluster(
mView, currentCluster, direction);
}
void doDie() {
checkThread();
if (LOCAL_LOGV) Log.v(mTag, "DIE in " + this + " of " + mSurface);
synchronized (this) {
if (mRemoved) {
我们平时最常用的大概就是刷新布局了。当异步数据请求完毕的时候,要填充数据时就要刷新布局,checkThread就生效了。
由此可见 只要 ViewRootImpl构造函数中存储的线程和调用刷新时的线程一致,那么就不会报错。所以也并不是不能在子线程刷新布局,只要ViewRootImpl构造的时候也在同一个子线程就可以了。但是一般不建议这么做。至于ViewRootImpl什么时候创建实例,篇幅比较长,我们在讲Activity启动的时候再说。
在Android开发中,当尝试在非主线程修改UI时会抛出Onlytheoriginalthreadthatcreatedaviewhierarchycantouchitsviews.异常。这个异常源于ViewRootImpl类的checkThread方法,该方法确保对UI的操作在正确的线程中执行。常见的触发checkThread的方法包括requestLayout()、requestFitSystemWindows()等。虽然理论上可以确保ViewRootImpl在子线程中创建并在同一子线程中更新,但通常不推荐这样做,因为这违反了Android应用的主线程原则。
1709

被折叠的 条评论
为什么被折叠?



