昨天同事问我一个问题,在Thread中去更新一个Button的状态可行吗?我说当然不行。他告诉我说,那为什么他的程序不会奔溃,我过去一看果然没有奔溃,甚是奇怪。难道是我记错了?于是我Google了一下。发现Android的开发文档中确实说这样是不行的啊。
http://developer.android.com/guide/components/processes-and-threads.html
- Do not access the Android UI toolkit from outside the UI thread
public void onClick(View v) { new Thread(new Runnable() { public void run() { Bitmap b = loadImageFromNetwork("http://example.com/image.png"); mImageView.setImageBitmap(b); } }).start(); }
it violates the second rule of the single-threaded model: do not access the Android UI toolkit from outside the UI thread—this sample modifies the ImageView
from the worker thread instead of the UI thread. This can result in undefined and unexpected behavior, which can be difficult and time-consuming to track down.
再看看我同事的代码:
public class MainActivity extends Activity { private Button btn; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btn = (Button)findViewById(R.id.btn); startAThread(); } private void startAThread() { new Thread(new Runnable() { @Override public void run() { btn.setText("Hello Michael!"); } }).start(); } }
没有发现什么大的区别,唯一的区别就在于执行的地方不太一样,Google官方文档是在onClick的时候去执行,而同事的代码是在onCreate的时候执行。于是我做了一些修改,增加一个onClick事件:
public class MainActivity extends Activity { private Button btn; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btn = (Button)findViewById(R.id.btn); btn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { startAThread(); } }); startAThread(); } private void startAThread() { new Thread(new Runnable() { @Override public void run() { btn.setText("Hello Michael!"); } }).start(); } }
果然,程序在onClick的时候crash了。
但是为什么呢?在onCreate的时候去不会crash呢?
我们看一下错误:
这个错误相信大家都有遇到过,意思就是说不能在非UI线程中去更新UI控件。顺着这个错误,
到源码中去查找一下ViewRootImpl.java这个文件。你可以看到这个异常:
void checkThread() { if (mThread != Thread.currentThread()) { throw new CalledFromWrongThreadException( "Only the original thread that created a view hierarchy can touch its views."); } }
这个方法就是用来判断更新UI控件的时候是否在UI线程中进行,如果不是的话就会报错。也就是onCreate的时候这个判断没有被执行到,因此onCreate中启动的Thread并不会导致程序的crash。
不知道这算不算Google的一个bug呢,虽然这样没有什么问题,但是也不建议大家这样做。因为这样可能会卡住UI线程,而且也没有必要这样做,Activity.runOnUiThread,View.Post,或者麻烦一点用AsyncTask,Handler这样的方式来处理都是Google比较推荐的做法。
相关推荐
C# 跨线程访问UI线程控件 在C#中,由于使用线程和调用UI的线程属于两个不同的线程,如果在线程中直接设置UI元素的属性,此时就会出现跨线程错误。 下面介绍两种解决方案 第一种:使用控件自带的Invoke或者...
qt编程_在子线程中更新UI界面
WPF 使用线程更新绑定数据的UI控件值,从而使得界面不卡顿。
通过委托,在子线程更新主线程UI界面
主要介绍了C#子线程更新UI控件的方法,在桌面应用程序中控制UI界面有着不错的实用价值,需要的朋友可以参考下
pdf版
在.NET中如需在非UI线程中改变UI控件属性时,CLR会抛出异常,提示无法在非UI线程中更新界面上的控件(Cross-thread operation not valid)。一般情况下有两种解决办法。
一个用多线程在UI线程内安全更新UI界面控件来避免UI访问冲突的程序
下面小编就为大家分享一篇浅谈C#跨线程调用窗体控件(比如TextBox)引发的线程安全问题,具有很好的参考价值,希望对大家有所帮助
dialog库,可以在任意类内调用,子线程或ui线程内均可显示
使用开发工具为VS2013;.net:4.0 多线程异步刷新ui界面,实时获取任务进度并进行反馈。
在MFC 子线程中使用UI(控件)退出时死锁或者超时处理参考
c#中跨线程调用windows控件 c#中跨线程调用 c#中跨线程调用UI控件 c#中跨线程调用UI c#中跨线程调用windows控件 c#中跨线程调用 c#中跨线程调用UI控件 c#中跨线程调用UI
C#经常会遇到UI线程被占用导致的界面卡顿,控件反应缓慢,局部停顿导致全界面停顿,这都是因为单一UI线程导致的,现在使用C#自动生成winform都是单一UI线程,想要多UI线程只能自己手动添加
子线程操作主线程控件,不卡顿,简易高效,代码人人看的懂
在C# 的应用程序开发中, 我们经常要把UI线程和工作线程分开,防止界面停止响应, 同时我们又需要在工作线程中更新UI界面上的控件。但直接访问会出现“线程间操作无效”的情况,因为.NET禁止了跨线程调用控件, 否则...
近期遇到界面中执行一些后台任务时界面卡死的情况,解决了在这里记录下。 PyQt PyQt简介 PyQt是Qt的python接口,PyQt的文档较少,但接口和函数可以完全参照Qt,继承了Qt中大量的控件以及信号机制,十分方便。以下...
C# 线程访问UI 使用代理和 Invoke方法,保证在UI线程中访问UI,实现了线程UI访问安全性。 ---简单实用
子线程访问UI线程控件的方法 MethodInvoker Invoke
自定义圆形进度控件,线程安全可靠,可在线程中自动更新ui,使用方便,功能强大