在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线程对它进行修改的,一定会报错!

这就是多线程编程,有时候你运行若干次,结果正确,并不表明你的逻辑就是对的。我们一定要遵循代码的规范,保持清晰的思维。