本质
是ViewRootImple的checkThread造成的奔溃 requestLayout时会调用checkThead()
View子线程刷新Ui不一定奔溃
奔溃的本质是ViewRootImpl 里面的checkThread奔溃
View.invalidate --> ... --> parent.invalidateChildInParent --> ViewRootImpl.invalidateChildInParent --> checkThread();
void checkThread() { if (mThread != Thread.currentThread()) { throw new CalledFromWrongThreadException( "Only the original thread that created a view hierarchy can touch its views."); } }
添加width会导致奔溃 mLayout!=null
fun test1(view: View) { MyLogger.hLog().e("test1") Thread { MyLogger.hLog().e("test1_start") // Thread.sleep(2000) binding.tv.width = 1000 binding.tv.text = "444444444" MyLogger.hLog().e("test1_end") }.start() }
TextView
if (mLayout != null) { checkForRelayout(); }
ViewRootImpl
@Override public void requestLayout() { if (!mHandlingLayoutInLayoutRequest) { checkThread(); mLayoutRequested = true; scheduleTraversals(); } }
弹Toast会奔溃
子线程没有Looper
onResume
onresume时并没有测量绘制完成 在子线程更新ui不一定会奔溃 除非做了耗时任务 或者调用invialte
View绘制调用流程
见印象笔记
handleResumeActivity performResumeActivity --> r.activity.performResume(r.startsNotResumed, reason); --> mInstrumentation.callActivityOnResume(this); --> activity.onResume(); wm.addView(decor,l)(WindowManagerImpl) --> root = new ViewRootImpl(view.getContext(), display); -->mViews.add(view)//DecorView mRoots.add(root)//ViewRootImpl mParams.add(wparams) //WindowManager.LayoutParams --->root.setView(view,wparams,panelParentView,userId) ViewRootImpl.setView () -->requestLayout(); -->scheduleTraversals ---> mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); -->doTraversal() -->performTraversals() 绘制View --> res = mWindowSession.addToDisplayAsUser // 将窗口添加到WMS上面 WindowManagerService --> 事件处理 --> view.assignParent(this); // getParent ViewRootImpl
ImageView
ImageView xml width=100 height=100
点击子线程修改背景也会奔溃