工作中有一个需求,先弹出ProgressDialog的进度条,再做一下耗时操作,为了模拟耗时,于是直接使用了Thread.sleep(),代码很简单:
但是奇怪的是,实际效果并没有是先显示ProgressDialog,后Sleep,而是直接先Sleep,再弹出进度条,这个就太奇怪了。
按理来说,中间不掺杂任何异步操作,也不涉及到子线程等等,但是效果却不是按照同步进行。
于是进行了一番尝试,分别使用了Toast,PopupWindow来尝试了一下:
然后发现结果依然是先sleep,再展示view 。
外部代码看起来很简单,所以只能去内部看一下源码,以ProgressDialog为例:
首先来说,Dialog的对象创建不可能是异步的,首先比较怀疑是对应的show()方法有问题,
那我们首先来看一下Show(),其实里面废话比较多,直接看主要代码:
其实可以看到,通过调用windowManager.addView来更新界面布局。
那么接下来进行测试:
结果显而易见,就是会先sleep,再addView()。
那么就说明addView()本身是一个异步操作,我们进去看一眼ViewGroup的addView()方法:
至于为什么addView()是异步,暂时搁置。
然后看一下WindowManager的addView(),实际上就是WindowManagerImpl,进去看一下:
接下来就只能看ViewRootImpl的setView()方法了:
其中可以看到,也是调用了requestLayout():
再进入到ViewRootImpl的scheduleTraversals(),这个方法本身是一个异步方法:
首先看一下mTraversalRunnable,它 是一个 Runnable 对象:
这里面最终会调用performTraverslas()方法,这个方法其实就是View工作流程的核心方法,会分别调用measure,layout,draw方法,从而刷新界面。
不过我们还是要看一下scheduleTraversals的异步问题,其实主要是在于mChoreographer对象,通过postCallback()将Runnable,:
接下来我们进去看一下,通过源码可以看到postCallback其实调用了postCallbackDelayedInternal():
核心代码在于这里面:
可以看到无论是哪一种情况,都会针对message进行msg.setAsynchronous(true);的处理,
那么表示该message没有target,主线程的Handler添加SyncBarrier,这样其实就相当于让其他
的Message全部被delay掉,只保证view能够最先被加载,没有使用
sendMessageAtFrontOfQueue(),是因为我们也可以改动方法,所以保证View最先被执行的
方式就是将Message给setAsynchronous(true)。
参考: