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();

同樣能夠正常運行

發佈了35 篇原創文章 · 獲贊 4 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章