今天闲来无事,突然想来一个问题,我在子线程中更新UI操作会怎么样?当然,老司机肯定知道,这样应用会抛出异常的,然后我做了这个测试,首先我在oncreat方法中做了这一个操作如下图
满心欢喜的运行测试项目,当时内心期待会抛出异常,但是,但是,万万没想到,程序竟然正常,我竟然惊呆了,这是怎么回事,难道是那些传说是假的?我内心有点懵逼了,我转念一想,这个时候UI是不是还没创建呢?oncreat方法中到底做了什么呢?我点开源码去看了一下,
发现oncreat方法中仅仅只是做了配置参数的保存,也就是说这个时候视图并没有创建,那么视图在什么时候创建呢?,我们知道,当生命周期调用了onresume方法时,这个时候activity才会展示在我们眼前,那么这个时候视图才会真正的创建了,也就说viewRootImp这个时候才会添加到decorview中,那么到底是不是在onresume方法中开始显示视图呢?我们看一段代码。
这是activity启动流程中一段代码,了解activity启动流程一定熟悉这段代码,我们可以看到在这里,视图才会真正的展示出来。也就是说在oncreat方法中去调用线程更新UI,这个时候视图还没有创建,所以才会出现上述现象,那么我们将上述方法移到onresume方法中去试试。结果,结果真的出现了
看到这个,我们知道了,原来诚不欺我。那么问题来了,为什么会这样了,也就是为什么不能再子线程中更新UI呢?我们看看更新UI做了什么
继续探究checkForRelayout()方法,
可以看到,每次都会是重绘界面UI,那么我们可以猜一下,是不是因为重绘机制中出现这个限制呢?带着这个疑问我们去看看重绘源码,
没有找到,但是可以看到调用了viewParent的重绘子view的方法,这个viewParent肯定是ViewGroup l 那么我们继续看ViewGroup 中的重绘机制的方法,
凉凉,什么都没看到,头皮发麻,但是突然想到这个方法是实现的啊,那么限制操作是不是在实现之前就做了呢,
没问可以看到调用了parent的重绘方法,那么我们结合上面代码可以知道,这个parent可能是viewRootImp也可能是view,也就是说,这个方法实际是在viewRootImp或者view当中的,首先我们去view中全局搜索这个方法发现没有这个方法,那么可以断定这个方法只能是viewRootImp当中的了,那么我们去看看。
看标记的地方,在重绘开始之前其实就在检查线程了,果然,这一步还是找到了,那么我们猜想慢慢有了结论,现在继续去看这个标记中的方法到底干了什么,
首先检查当前线程如果不是主线程,直接抛出上述异常,也就是我们上面在onresume中操作时候抛出的异常,Soga,这个过程终于被我找到了。
好了,现在我们来总结下,首先我们在oncreat方法中实现子线程更新UI,这个时候是不会报异常的,因为这个时候视图还没有显示出来,然后在onresume方法操作,这个异常抛出来,那么这个异常怎么产生的呢,因为更新UI是需要重绘的,在viewRootImp中的重绘机制发现,在重绘之前是需要检查线程的,如果当前线程不是主线程的话,那么就会抛出这个异常,但是还有一种方法说,在子线程中更新UI是不安全的,对于这个解释一下,首先这个观点是对的,因为如果两个子线程,其中一个更新UI了,例如:tv.setText("好"),但是另一个子线程也去更新UI,tv.setText("坏"),那么这个UI就会不断被修改,这个肯定是我们不愿意看到的,在主线中因为只有一个主线程,不会存在其他主线程抢占资源去读写UI操作,所以综合上述两个观点,为什么子线程中不能更新UI操作了!!!
网友评论