`

关于在非UI线程中更新UI控件不会crash的问题

阅读更多

昨天同事问我一个问题,在Thread中去更新一个Button的状态可行吗?我说当然不行。他告诉我说,那为什么他的程序不会奔溃,我过去一看果然没有奔溃,甚是奇怪。难道是我记错了?于是我Google了一下。发现Android的开发文档中确实说这样是不行的啊。

http://developer.android.com/guide/components/processes-and-threads.html

 

  1. 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比较推荐的做法。

 

 

 

 

  • 大小: 7.8 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics