【Android开发经验】来来来,同学,咱们讨论一下“只能在UI主线程更新View”这件小事
来源:互联网 发布:mac开机咚一声怎么关 编辑:程序博客网 时间:2024/05/15 20:45
”只能在UI主线程中更新View“。
这句话很熟悉吧?
来来,哥们,看一下下面的例子
代码这么写,不是逗比吗!肯定崩啊!但是,如果你试一下,你会发现,绝大多数是不会崩的。至于极少数会崩溃的原因,我一会再说。
你可能会很疑惑,不是”只能在UI主线程中更新View“吗?你这个在子线程里面更新View,为什么不会崩呢?
那么,你再看下面的代码,这样写就肯定崩
调用的代码和上面的一样,只不过是我们是给一个Button设置了点击事件,然后自己手动调用的,这样就肯定会崩溃,报什么错呢?
报下面的错
意思就是说,只有创建View层次结构的线程才能修改View,我们在非UI主线程里面更新了View,所以会报错。
但是你还没说为什么第一种调用方法为什么不报错啊!!!
先别着急,先看一下上面的报错信息,里面有好多东西可以研究呢!
我们先从下面开始看,首先是LooperThread.run()报错了,为什么报错呢,再往上,因为我们调用setText了,再往上就是TextView.checkForRelayout,然后上面是invalidate,我们修改了文字,肯定要invalidate啊,谁调用的呢?原来是ViewGroup.invalidateChild,往上找啊找,终于找到了罪魁祸首,ViewRootImpl.checkThread()报错了!
ViewRootImpl是什么玩意?这个玩意可很强大,你现在就只需要知道它能做很多和界面有关的事情就ok了,其实我对这个类也是只了解一点点。。。
checkThread()到底做了什么啊,就报错!ViewRootImpl是一个隐藏类,我们只能去看源码,源码如下
就是这个方法报错了!ViewRootImpl是依附在AttachInfo这个类上的,而AttachInfo是View得一个隐藏类,你在Eclipse里面是看不到的,需要直接看framework得源码。所以说,这里的Thread.currentThread()是UI主线程,就是这里判断是不是我们在非UI主线做了修改VIew的操作。
那么问题又来了,为啥第一种方式,不会报错呢!!!
ok,不要抓狂,咱们先回顾一下Activity的生命周期。靠!和Activity的生命周期怎么又扯上关系了?
Activity在onCreate中进行界面的数据准备,onStart()之后,Activity的界面就对用户可见了,那么知道这些有什么用呢?当然有用!上面两种调用方式的差别就在调用的时机不同!一个是在onCreate中开启线程调用,一个是我们手动调用,暗示着,第二种方法是在onResume之后调用!
这种调用时机的差别就决定了代码中setText的意义!
第一种做法中,虽然是在子线程中setText,但是这时候View还没画出来呢,所以并不会调用之后的invalidate,而相当于只是设置TextView的一个属性,不会invalidate,就没有后面的那些方法调用了,归根结底,就不会调用ViewRootImpl的checkThread,也就不会报错。而第二种方法,调用setText之后,就会引发后面的一系列的方法调用,VIew要刷新界面,ViewGroup要更新布局,计算子View的大小位置,到最后,ViewRootImpl就会checkThread,就崩了。
所以,严格上来说,第一种方法虽然在子线程了设置View属性,但是不能够归结到”更新View“的范畴,因为还没画出来呢,就没有所谓的更新。
那么,为啥说绝大多数不会报错呢?这是因为,如果我们在onCreate开启子线程之后,在子线程又执行了耗时操作,比如Thread.sleep,那么子线程再调用setText的时候,就会崩溃,因为这时候onStart、onResume都执行了,你再想在子线程更新View,那么门都没有!
不知道有没有同学会这样想:我记得在子线程里面用Toast也会报错,加上Looper.prepare和Looper.loop就可以了,这里可以这样做吗?
答案当然是不可以。
Toast和View本质上是不一样的,Toast在子线程报错,是因为Toast的显示需要添加到一个MessageQueue中,然后Looper取出来,发给Handler调用显示,子线程因为没有Looper,所以需要加上Looper.prepare和Looper.loop创建一个Looper,但是实质上,这还是在子线程调用,所以还是会报错的!
那么为什么Android要求只能在UI主线程中更改View呢?这就要说到Android的单线程模型了,因为如果支持多线程修改View的话,由此产生的线程同步和线程安全问题将是非常繁琐的,所以Android直接就定死了,View的操作必须在UI线程,从而简化了系统设计。
ok,希望上面说到的这些能对你有所帮助。
转载请注明出处:http://blog.csdn.net/zhaokaiqiang1992
- 【Android开发经验】来来来,同学,咱们讨论一下“只能在UI主线程更新View”这件小事
- 【Android开发经验】来来来,同学,咱们讨论一下“只能在UI主线程更新View”这件小事
- 【Android开发经验】来来来,同学,咱们讨论一下“只能在UI主线程更新View”这件小事
- “只能在UI主线程更新View”这件小事,android ui
- Android 更新UI 只能在主线程?
- Android UI更新只能在主线程工作是个美丽的谎言?
- 主线程负责交互(事件处理)和UI绘制(onDraw), 只能在主线程访问View, 其他线程不能直接访问View (Android)
- 在主线程更新UI
- Android显示系统之View与SurfaceView更新屏幕的区别.对于View.则是在UI主线程中更新画面.SurfaceView更新屏幕,是在非UI线程(主线程)中实现SurfaceHolde
- 在Android中,非主线程不能更新UI
- Android 在子线程中更新主线程UI
- Android AsyncTask使用心得及错误处理-只能在主线程改变UI组件
- Android之UI主线程更新问题
- Android只有主线程才能更新UI?
- Android非主线程更新UI
- Android新线程中更新主线程UI中的View方法汇总
- Android新线程中更新主线程UI中的View方法汇总
- 在UI线程(主线程)外实现UI更新
- ES5.0 新版本的特性与改进
- 数据结构看书笔记(十二)--排序算法
- SpringBoot Data JPA 实战
- Textview的getLineCount返回0
- HDU 1387 Team Queue (嵌套队列)
- 【Android开发经验】来来来,同学,咱们讨论一下“只能在UI主线程更新View”这件小事
- 算法导论——2.1-3线性查找
- 【Linux】crontab定时执行任务
- Java使用MD5对文件进行签名
- 不能打开到主机的连接,在端口1521:连接失败的解决方法
- 计蒜课--跳跃游戏
- 图片不拉伸方法
- shiro登录过程分析
- 手机无线连接电脑