首页 > 代码库 > UI更新 CalledFromWrongThreadException

UI更新 CalledFromWrongThreadException

在写Activity的时候需要进行UI更新,遇到了一些问题,记录一下:

1、单线程 vs 多线程。

单线程的好处是逻辑简单,不需要考虑线程间通讯问题,坏处是在进行诸如下载、更新、读写的操作的时候会阻塞当前线程,造成假死现象,无论是用户体验还是测试监控都是十分糟糕的选择。因此在这里我把实际处理的过程用新的线程处理,只有在UI需要更新的时候再通知UI线程更新。多线程时候线程间通讯有几种实现机制,Android的多线程也有两种写法,一种是按照Java原先的风格写runnable,貌似实际上没有新开线程,另一种是利用handler消息队列的方式,实际上有新开线程。具体的知识百度一下。注意线程循环在线程开始的位置需要加一行代码

Looper.prepare();

目的是准备消息循环,即使你没有用到handler也没有用到Looper.loop()。否则会报错。

2、UI更新。

在选择了多线程之后,写了一个线程来处理数据,其中涉及到更新progressbar的操作,直接操作的话会报错(CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.),提示必须要在UI线程才能进行UI操作,这个是Android系统限制。可以有几种方式,例如利用1中线程间通讯的方法通知UI线程的handler更新UI,还有一种不那么规范但是非常方便的方法:

Activity.this.runOnUiThread(new Runnable() {                            public void run() {                                //do something                                                            }                        });

将要进行UI更新的代码抛给UI线程。在只需要少量地方集中处理UI的时候非常方便,缺点是如果很多地方要用,这个代码无法复用,就成了垃圾代码。

3、线程中断。

通常来说线程运行到最后就会自然结束,因此我们可以通过一定的方法人为让线程立即运行到最后。看了一下没有特别好的方法,主要有两种:interrupt和flag。需要线程中断的情况通常是不知道线程执行结束时间但要强行中断,或者线程本身是个死循环需要外部条件触发中断。我这次遇到的是后者,我想人为结束一个长循环的线程。interrupt的做法是每次运行都等待一个时间,有点像键盘中断在指令中的地位(指令结束后等待键盘中断),需要设定一个等待(sleep)时间。我这里是一个长循环,所以就用了设定flag的方式,线程运行时先初始化标志位paused,然后在循环的时候每一次循环开头都判断标志位(因为这个是长循环而不是死循环,不能用while(!paused)这样的方式),外部要求结束线程时就把标志位paused置true并跳出循环,等待线程自然结束。

需要注意的是,Thread.stop()这个方法是不起作用的。