android.view.ViewRootImpl$CalledFromWrongThreadException异常处理

一般情况下,我们在编写android代码的时候,我们会将一些耗时的操作,比如网络访问、磁盘访问放到一个子线程中来执行。而这类操作往往伴随着UI的更新操作。比如说,访问网络加载一张图片

new Thread() {
				@Override
				public void run() {
					try {
						URL url = new URL(path);
						HttpURLConnection connection = (HttpURLConnection) url
								.openConnection();
						// 设置请求方式
						connection.setRequestMethod("GET");
						// 设置超时时间
						connection.setConnectTimeout(10000);
						int code = connection.getResponseCode();
						if (code == 200) {
							InputStream is = connection.getInputStream();
							Bitmap bitmap = BitmapFactory.decodeStream(is);

							iv_beauty.setImageBitmap(bitmap);
						} else {
							Toast.makeText(getApplicationContext(), "图片获取失败!",
									0).show();
						}
					} catch (Exception e) {
						Toast.makeText(getApplicationContext(), "图片获取失败!", 0)
								.show();
					}
				}

			}.start();


如果是这样去操作,就会抛出

10-20 02:50:38.219: W/System.err(497): android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

后边那句英文的大概是说只有创建了UI对象的主线程才能去修改UI

原因在于,在android里面不是线程安全的,所以android有阻止子线程更新组件的机制。

于是我们应该将UI的更新交给主线程来完成,如何去完成,android为我们提供了一套消息处理机制。

它大概的实现步骤是这样子的

1.子线程利用Handler类发送一条消息,消息会被送完主线程的消息队列里

2.主线程里边有一个叫looper的轮询器,它会循环遍历消息队列

3.如果loopler发现消息队列里有新的消息,android就会调用Handler的handleMessage()方法来处理消息

我们来看一下实例代码吧

1.首先我们先定义好两个常量

private static final int UPDATE_UI = 1;
private static final int ERROR = 2;

2.修改上边那段有异常的代码

new Thread() {
				@Override
				public void run() {
					try {
						URL url = new URL(path);
						HttpURLConnection connection = (HttpURLConnection) url
								.openConnection();
						// 设置请求方式
						connection.setRequestMethod("GET");
						// 设置超时时间
						connection.setConnectTimeout(10000);

						// connection.setRequestProperty(field, newValue)

						int code = connection.getResponseCode();
						if (code == 200) {
							InputStream is = connection.getInputStream();
							Bitmap bitmap = BitmapFactory.decodeStream(is);

							// 告诉主线程一个消息,帮我更新ui,内容:bitmap
							Message msg = new Message();
							// 消息的代号,是一个int类型
							msg.what = UPDATE_UI;
							// 要传递的消息对象
							msg.obj = bitmap;
							// 利用handler发送消息
							handler.sendMessage(msg);
						} else {
							Message msg = new Message();
							msg.what = ERROR;
							handler.sendMessage(msg);
						}
					} catch (Exception e) {
						e.printStackTrace();
						Message msg = new Message();
						msg.what = ERROR;
						handler.sendMessage(msg);
					}
				}

			}.start();

3.在主线程中新建一个Handler类来处理消息

// 主线程创建消息处理器
	Handler handler = new Handler() {
		// 但有新消息时调用
		@Override
		public void handleMessage(Message msg) {
			if (msg.what == UPDATE_UI) {
				// 获取消息对象
				Bitmap bitmap = (Bitmap) msg.obj;
				// 更新UI
				iv_beauty.setImageBitmap(bitmap);
			} else if (msg.what == ERROR) {
				// Toast也是属于UI的更新
				Toast.makeText(getApplicationContext(), "图片获取失败!", 0).show();
			}
		}
	};


再次运行代码,就不会抛出android.view.ViewRootImpl$CalledFromWrongThreadException异常了



另外我们还可以利用Activity里边提供的一个runOnUiThread()方法来更新UI

runOnUiThread()的官方文档是这么描述其作用的:

Runs the specified action on the UI thread. If the current thread is the UI thread, then the action is executed immediately. If the current thread is not the UI thread, the action is posted to the event queue of the UI thread.

于是我们只需要修改上边有异常那段代码:

new Thread() {
				@Override
				public void run() {
					try {
						URL url = new URL(path);
						HttpURLConnection connection = (HttpURLConnection) url
								.openConnection();
						// 设置请求方式
						connection.setRequestMethod("GET");
						// 设置超时时间
						connection.setConnectTimeout(10000);

						// connection.setRequestProperty(field, newValue)

						int code = connection.getResponseCode();
						if (code == 200) {
							InputStream is = connection.getInputStream();
							final Bitmap bitmap = BitmapFactory
									.decodeStream(is);

							// // 告诉主线程一个消息,帮我更新ui,内容:bitmap
							// Message msg = new Message();
							// // 消息的代号,是一个int类型
							// msg.what = UPDATE_UI;
							// // 要传递的消息对象
							// msg.obj = bitmap;
							// // 利用handler发送消息
							// handler.sendMessage(msg);

							runOnUiThread(new Runnable() {

								@Override
								public void run() {
									iv_beauty.setImageBitmap(bitmap);

								}
							});
						} else {
							// Message msg = new Message();
							// msg.what = ERROR;
							// handler.sendMessage(msg);
							runOnUiThread(new Runnable() {

								@Override
								public void run() {
									Toast.makeText(getApplicationContext(), "图片获取失败!", 0).show();

								}
							});
						}
					} catch (Exception e) {
						e.printStackTrace();
						// Message msg = new Message();
						// msg.what = ERROR;
						// handler.sendMessage(msg);
						runOnUiThread(new Runnable() {

							@Override
							public void run() {
								Toast.makeText(getApplicationContext(), "图片获取失败!", 0).show();

							}
						});
					}
				}

			}.start();

同样能够正常运行

Android开发中,当从非UI线程中访问UI组件时,就会抛出 `android.view.ViewRootImpl$CalledFromWrongThreadException` 异常。这是因为Android中的UI组件不是线程安全的,只能在主线程中更新UI。 要解决这个问题,有以下几种方法: 1. 使用 `runOnUiThread()` 方法 `runOnUiThread()` 方法可以在主线程中执行一个Runnable对象。因此,如果需要在非UI线程中更新UI,可以使用 `runOnUiThread()` 方法来实现。 例如: ``` runOnUiThread(new Runnable() { public void run() { // 在这里更新UI } }); ``` 2. 使用 `Handler` 类 `Handler` 类可以用于在不同的线程之间传递消息和任务。通过在主线程中创建一个 `Handler`,并将其传递给子线程,就可以在子线程中更新UI。 例如: ``` Handler handler = new Handler(Looper.getMainLooper()); // 在子线程中发送消息 new Thread(new Runnable() { public void run() { Message message = handler.obtainMessage(); message.what = 1; handler.sendMessage(message); } }).start(); // 在主线程中处理消息 handler = new Handler(Looper.getMainLooper()) { public void handleMessage(Message msg) { // 在这里更新UI } }; ``` 3. 使用 `AsyncTask` 类 `AsyncTask` 类可以用于在后台线程中执行耗时操作,并在主线程中更新UI。在 `AsyncTask` 的 `doInBackground()` 方法中执行耗时操作,在 `onPostExecute()` 方法中更新UI。 例如: ``` private class MyTask extends AsyncTask<Void, Void, Void> { protected Void doInBackground(Void... params) { // 在这里执行耗时操作 return null; } protected void onPostExecute(Void result) { // 在这里更新UI } } // 在主线程中执行异步任务 new MyTask().execute(); ``` 总之,无论使用哪种方法,都需要保证更新UI的操作在主线程中执行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值