看到这样的标题……估计N多人会说我是逗比…………因为很多盆友在学习Android(特别是从4.0之后开始入门的)的时候都会常看见或听到别人说我们更新UI呢要在UI线程(或者说主线程)中去更新UI,不要在子线程中更新UI,而Android官方呢也建议我们不要在非UI线程直接更新UI,为什么呢?借助Android官方的一句话来说就是:
“The Android UI toolkit is not thread-safe and the view must always be manipulated on the UI thread.”
因此,很多童鞋会有这么一个惯性思维:在非UI线程中不能更新UI!既然Android不建议我们这么做,那其必定会对我们在code时做一些限制,比如当我们尝试运行如下代码时:
为了把情况说明,这里我也将xml布局文件代码贴出来:
当我们运行上述代码后,你便会在Logcat中得到如下error提示:
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
这句话非常简单,而且……我相信每个做Android开发到一定时间的盆友都碰到过,Android通过检查我们当前的线程是否为UI线程从而抛出一个自定义的AndroidRuntimeException来提醒我们“Only the original thread that created a view hierarchy can touch its views”并强制终止程序运行,具体的实现在ViewRootImpl类的checkThread方法中:
这就是Android在4.0后对我们做出的一个限制。写这篇Blog的具体原因来自凯子哥的一篇博文:来来来,同学,咱们讨论一下“只能在UI主线程更新View”这件小事,鉴于凯子哥如此好学,我想想呢也许很多盆友也有类似疑问:究竟TM到底能不能在非UI线程中更新UI呢?同时也为了引出我对在非UI线程更新UI方法的一些总结,我决定在3/4之前先撸一篇Blog扫清障碍先。首先,我先回答几个问题包括凯子哥的:
OK,这里我们再来看一下上面的一段代码,在线程中我调用了Thread.sleep(200);来让我们的匿名线程暂停了200ms,如果……假如……我们去掉它的话……………………会发生什么?来试试:
这时你会发现我们的代码TM地正确执行了!而且我们的TextView正确显示出了“OtherThread”文本!看到屏幕上的这11个英文字母我相信大家又把刚放出来又吸进去的屁再一次地放了出来…………这就是凯子哥Blog中提到的问题,我们成功地在非UI线程中更新了UI。其实这里最最根本的原因是我们并没有checkThread我们的当前线程,而我在文章最开始的代码中通过Thread.sleep(200)暂停了一小段时间,这里为什么回暂停线程一段时间?在这段时间的背后Android背地里背着我们都干了什么?数百头母驴为何半夜惨叫?小卖部安全套为何屡遭黑手?女生宿舍内裤为何频频失窃?连环强奸母猪案,究竟是何人所为?老尼姑的门夜夜被敲,究竟是人是鬼?数百只小母狗意外身亡的背后又隐藏着什么?这一切的背后, 是人性的扭曲还是道德的沦丧?是性的爆发还是饥渴的无奈?抱歉……磕个药,上面我们讲到,我们能正确以上述代码的方式在非UI线程中更新UI而不报错,那么原因也许只有一个,那就是没有执行checkThread方法去检查我们的当前线程……但是,细看调用checkThread方法的调用方法们你就会发现,TM全是跟View创建生成相关:
也就是说一旦我们尝试去对我们的控件进行生成,这些方法其中一个必然会被调用,这时候很多朋友就会蛋疼了…………但是,请不要被checkThread方法的思维所束缚,这时候你该扩大你的思维范畴,既然checkThread方法属于ViewRootImpl的成员方法,那么会不会是此时我们的ViewRootImpl根本就没被创建呢?怀着这个出发点,我们再度审视ActivtyThread调度Activity生命周期的各个环节,首先看看performLaunchActivity方法中的处理:
performLaunchActivity方法中目测木有我我们想要的信息,其创建了Activity并调度了Create和Start的逻辑处理,那我们看看callActivityOnCreate方法呢:
没错,可以执行!首先我们这里的是个线程,其次这里要涉及framework对UI事件处理的方式,我们在Android翻页效果原理实现之引入折线中曾说过Android对UI事件的处理需要依赖于Message Queue,当一个Msg被压入MQ到处理这个过程并非立即的,它需要一段事件,我们在线程中通过Thread.sleep(200)在等,在等什么呢?在等ViewRootImpl的实例对象被创建,有关于GUI中Message Queue的处理如有机会我会浓缩在《深入理解 Android GUI 框架》系列中,这里就暂且先不说了,那么又有同学会问了!纳尼,既然ViewRootImpl还未被创建那么为什么会能绘制出文本?!!!如果你有这个疑问,我只能说你观察细致问得好,但是,这个问题我不打算解答,留给各位,上面我其实就在教大家如何去寻找原因了,渔已授之于你所以就不再多说了~~~~既然我们找到了原因所在,那么我们该如何摆脱“The Android UI toolkit is not thread-safe and the view must always be manipulated on the UI thread.”这个噩梦呢?
欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) | 黑马程序员IT技术论坛 X3.2 |