Toast will not show before try/catch Thread.sleep()

工作中有一个需求,先弹出ProgressDialog的进度条,再做一下耗时操作,为了模拟耗时,于是直接使用了Thread.sleep(),代码很简单:

Toast will not show before try/catch Thread.sleep()_第1张图片
ProgressDialog

但是奇怪的是,实际效果并没有是先显示ProgressDialog,后Sleep,而是直接先Sleep,再弹出进度条,这个就太奇怪了。

按理来说,中间不掺杂任何异步操作,也不涉及到子线程等等,但是效果却不是按照同步进行。

于是进行了一番尝试,分别使用了Toast,PopupWindow来尝试了一下:

Toast will not show before try/catch Thread.sleep()_第2张图片
Toast


Toast will not show before try/catch Thread.sleep()_第3张图片
Popupwindow

然后发现结果依然是先sleep,再展示view 。

外部代码看起来很简单,所以只能去内部看一下源码,以ProgressDialog为例:

首先来说,Dialog的对象创建不可能是异步的,首先比较怀疑是对应的show()方法有问题,

那我们首先来看一下Show(),其实里面废话比较多,直接看主要代码:

Toast will not show before try/catch Thread.sleep()_第4张图片
show()部分代码

其实可以看到,通过调用windowManager.addView来更新界面布局。

那么接下来进行测试:

Toast will not show before try/catch Thread.sleep()_第5张图片
测试addView

结果显而易见,就是会先sleep,再addView()。

那么就说明addView()本身是一个异步操作,我们进去看一眼ViewGroup的addView()方法:

Toast will not show before try/catch Thread.sleep()_第6张图片
ViewGroup.addView()

至于为什么addView()是异步,暂时搁置。

然后看一下WindowManager的addView(),实际上就是WindowManagerImpl,进去看一下:

Toast will not show before try/catch Thread.sleep()_第7张图片
WindowManagerImpl.addView()


Toast will not show before try/catch Thread.sleep()_第8张图片
WindowManagerGlobal.addView()

接下来就只能看ViewRootImpl的setView()方法了:


Toast will not show before try/catch Thread.sleep()_第9张图片
ViewRootImpl.setView()

其中可以看到,也是调用了requestLayout():

Toast will not show before try/catch Thread.sleep()_第10张图片
requestLayout()

再进入到ViewRootImpl的scheduleTraversals(),这个方法本身是一个异步方法

Toast will not show before try/catch Thread.sleep()_第11张图片
scheduleTraversals()

首先看一下mTraversalRunnable,它 是一个 Runnable 对象:

Toast will not show before try/catch Thread.sleep()_第12张图片
mTraversalRunnable类
Toast will not show before try/catch Thread.sleep()_第13张图片
doTraversal()

这里面最终会调用performTraverslas()方法,这个方法其实就是View工作流程的核心方法,会分别调用measure,layout,draw方法,从而刷新界面。

不过我们还是要看一下scheduleTraversals的异步问题,其实主要是在于mChoreographer对象,通过postCallback()将Runnable,

Toast will not show before try/catch Thread.sleep()_第14张图片
scheduleTraversals

接下来我们进去看一下,通过源码可以看到postCallback其实调用了postCallbackDelayedInternal():

Toast will not show before try/catch Thread.sleep()_第15张图片
postCallbackDelayedInternal

核心代码在于这里面:

Toast will not show before try/catch Thread.sleep()_第16张图片
异步
Toast will not show before try/catch Thread.sleep()_第17张图片
scheduleFrameLocked()

可以看到无论是哪一种情况,都会针对message进行msg.setAsynchronous(true);的处理,

那么表示该message没有target,主线程的Handler添加SyncBarrier,这样其实就相当于让其他

的Message全部被delay掉,只保证view能够最先被加载,没有使用

sendMessageAtFrontOfQueue(),是因为我们也可以改动方法,所以保证View最先被执行的

方式就是将Message给setAsynchronous(true)。

Toast will not show before try/catch Thread.sleep()_第18张图片
setAsynchronous说明
Toast will not show before try/catch Thread.sleep()_第19张图片
示例图

参考:

Android View 深度分析requestLayout、invalidate与postInvalidate

Android事件机制详细解读

你可能感兴趣的:(Toast will not show before try/catch Thread.sleep())