view安卓面试宝典面试

Activity的生命周期 和 View的绘制流程 的关联

2021-10-24  本文已影响0人  慕尼黑凌晨四点

问题

我们都知道Android在子线程中更新UI会报错:

Only the original thread that created a view hierarchy can touch its views

但是,现在有如下代码,却可以正常运行。

private ImageView mImageView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mImageView = (ImageView)findViewById(R.id.iv);
    new Thread(new Runnable() {
        @Override
        public void run() {
            mImageView.setImageResource(R.drawable.ic_book);//更新 ui
        }
    }).start();
}

所以就从这个问题入手,回顾下Android中的绘制流程。

声明周期和绘制流程

我们知道Activity的声明周期是onCreate->onStart->onResume,View的绘制流程是 onMeasure->onLayout ->onDraw,而且,我们在activity的声明周期的这三个阶段是获取不到View的宽高信息的,也就是说View的绘制肯定是发生在activity声明周期onResume之后的,但具体是在哪个阶段就要从源码中找了。

ActivityThread

onCreateonStartonResume既然是个方法,肯定是从某个管理Activity声明周期执行的类中被调用的,这个类就是ActivityThread

handleLaunchActivity

handleStartActivity

handleResumeActivity

ViewRootImpl下的TraversalRunnable

TraversalRunnableViewRootImpl下的子类,所以以下这些方法都是写在ViewRootImpl下的。

所以就像Activity的生命周期相关方法是在ActivityThread中被调用一样,View的绘制流程相关方法被调用的源头就是ViewRootImpl

这三个方法就对应了View下的onMeasure,onLayout ,onDraw,所以会从ViewRootImpl开始,至上而下的开始绘制流程。

解答

再回到最开始的那个问题,再onCreate中开辟线程,竟然可以成功更新UI。

会一层一层往上执行requestLayout

if (mParent != null && !mParent.isLayoutRequested()) {
    mParent.requestLayout();
}

假设这时更新到decorView了,再去往上找ViewRootImpl,因为ViewRootImpl是在onResume之后root.setView()的时候才被设置为decor的parent,所以这个时候decor是没有parent的,所以自然会略过这行代码,直接开始更新了。

如果为这个线程加上延时,使其在view.assignParent(this)执行完之后,再调用requestLayout,就会执行到ViewRootImpl#requestLayout方法中来:

@Override
public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
        checkThread();
        mLayoutRequested = true;
        scheduleTraversals();
    }
}

void checkThread() {
    if (mThread != Thread.currentThread()) {
        throw new CalledFromWrongThreadException(
            "Only the original thread that created a view hierarchy can touch its views.");
    }
}

这个时候,如果线程不对的话,就会报“子线程不能更新UI”的异常了。

上一篇 下一篇

猜你喜欢

热点阅读