在Android的开发中,非UI线程不能操作UI线程中的控件,即UI是非线程安全的。
我在上一篇文章中这样说过:
它是非UI安全的,也就是说,不接受非UI线程的修改请求。当我们通过别的线程(非主线程或者说是非原始线程)来修改它的时候,
会抛出这个异常:android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
CalledFromWrongThreadException 这个字面上都理解过来了,是说这个请求来自于错误的线程。
Only the original thread that created a view hierarchy can touch its views 只有最初创建视图层次结构的线程才可以接触到这些视图。
有些朋友可以不太明白这样的解释,今天我又写了一个这样的例子,让我们来看一下。
为了方便测试,在布局中我只有一个TextView,我通过非UI线程来修改它上面显示的值。
print?
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
new Thread(new MyThread()).start();
}
class MyThread implements Runnable {
@Override
public void run() {
TextView txtMsg = (TextView) findViewById(R.id.txtMsg);
txtMsg.setText("Modify");
}
}
可能很多朋友都会正常完成修改TextView上面的值,所以对这个非UI线程来修改UI线程抱有怀疑和不解,因为我们明明是通过一个非UI的线程来修改成功的啊。
这里涉及到一个多线程的关系。我们知道,一个进程有了两条或两条以上的线程我们就可以叫它多线程了,它是多个线程可以同时工作,我来举个例子,
我到食堂里吃饭,师傅尽职地在窗口打菜,这就是两个线程,然后我点了一份TextView,里面装着Modify。最后我拿到这份TextView,回到座位上把它给吃了。
这个例子一切正常,所以什么异常都没有,然后第二天我又去了这个食堂。这天不知是我去得早了,还是这个师傅动作有些慢了。
我到食堂里吃饭,师傅一边XXX一边在炒菜,我惊呆了。然后他把炒好的TextView端上来,我抛出一个恶心反胃的异常,逃走了再也没有再来过这家食堂。
这两个例子,一个报错了一个没有,其实就在于我到食堂的时间,师傅是否已经做好了菜,就像上面的例子,UI线程和MyThead线程几乎是同时执行,
但后者的工作量太少了,更改了TextView上面的字就完成了,这里UI线程把这个UI展示出来,一切都很正常。但要是我们让MyThead在run的时候先睡眠几秒钟呢?
或者是弄一个按钮,点击按钮后MyThead才开始?这样都是在UI线程加载完成后才通过非UI线程对它进行修改的,一定会报错!
这就是多线程编程,有时候你运行若干次,结果正确,并不表明你的逻辑就是对的。我们一定要遵循代码的规范,保持清晰的思维。