Only the original thread that created a view hierarchy can touch its views. 刨根问底

在Android开发中,当尝试在非主线程修改UI时会抛出Onlytheoriginalthreadthatcreatedaviewhierarchycantouchitsviews.异常。这个异常源于ViewRootImpl类的checkThread方法,该方法确保对UI的操作在正确的线程中执行。常见的触发checkThread的方法包括requestLayout()、requestFitSystemWindows()等。虽然理论上可以确保ViewRootImpl在子线程中创建并在同一子线程中更新,但通常不推荐这样做,因为这违反了Android应用的主线程原则。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

开发过程中在子线程访问控件的时候会出现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启动的时候再说。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

闽农qq:994955138

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值