1. 程式人生 > >Handler的理解及android.view.ViewRootImpl$CalledFromWrongThreadException錯誤處理

Handler的理解及android.view.ViewRootImpl$CalledFromWrongThreadException錯誤處理

在前面的幾篇文章中,不管是AsyncTAsk方式,亦或是new一個Runable,都沒有涉及UI介面的更新(),今天在進行註冊反饋註冊資訊的時候,就發生了ViewRootImpl$CalledFromWrongThreadException異常,究其原因是因為android中的view和和控制元件不是執行緒安全的。

為此android已入了Handler訊息傳遞機制,來實現在新建的執行緒中操作UI介面。

資訊處理類(Handler)允許傳送和處理Message或Runable物件到其所線上程的MessageQuene中。Handler主要有以下作用:

(1)將Message或Runable應用Post() 或 sendMessage() 方法傳送到MessageQuene中,在傳送是可以指定延遲時間,傳送時間及要攜帶的Bundle資料,當MessageQuene迴圈到該Message時,呼叫相應的Handler物件的HandlerMessage()方法對其進行處理。

(2)在子執行緒與主執行緒進行通訊,也就是在工作執行緒中與UI執行緒進行通訊。

補充:在android中,一個執行緒對應一個Looper物件,而一個Looper物件又對應一個MessageQuene(訊息佇列,用於存放Message訊息,MessageQuene封裝在Looper中),在主執行緒中,系統會自動為主執行緒建立Looper物件,並開啟訊息迴圈,但在非主執行緒建立Handler物件(不加Looper.prepare()和Looper.Loop()方法來初始化一個Looper物件),會報Can't create handler inside thread that has not called Looper.prepare()

錯誤。

從飛天開放平臺獲取註冊資料的簡單程式碼:

package com.example.alitest;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import ECSConnecter.SigninConnecter;
import XMLReader.SigninData;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class MainActivity extends Activity implements Runnable {
	private Button button;
	private Handler handler;
	private TextView name;
	private TextView pwd;
	private EditText etName;
	private EditText etPwd;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		initView();
		button.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				Thread t = new Thread(MainActivity.this);		//建立新執行緒
		 		t.start();										//開啟執行緒

				handler = new Handler() {						//這個handler傳送的Message會被傳遞給主執行緒的MessageQueue。
					public void handleMessage(Message msg) {	//回撥
						if (msg.what == 1) {
							name.setText(msg.getData().getString("result"));
							pwd.setText(msg.getData().getString("ID"));
						}
						super.handleMessage(msg);
					}

				};
			}
		}); 
	}

	@Override
	public void run() {
		while (!Thread.currentThread().isInterrupted()) {
			/*
			 * 連線註冊程序
			 */
			Map<String, String> map = new HashMap<String, String>();
			map.put("name", etName.getText().toString());
			map.put("password", etPwd.getText().toString());
			SigninConnecter signconn = new SigninConnecter(map); 
			try {
				signconn.getXML();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			signconn.readXML();
			SigninData data = signconn.getData();
			Message m = handler.obtainMessage();	//獲取一個Message
			Bundle bundle = new Bundle();			//獲取Bundle物件	
			m.what = 1;								//設定訊息標識
			bundle.putString("result", data.getResult());
			bundle.putString("ID", data.getId());	//儲存資料
			m.setData(bundle);						//將Bundle物件儲存到Message中
			handler.sendMessage(m);					//傳送訊息

		}
	}
	/*
	 * 初始化控制元件
	 */
	private void initView() {
		name = (TextView) findViewById(R.id.textView1);
		pwd = (TextView) findViewById(R.id.textView2);
		etName = (EditText) findViewById(R.id.etName);
		etPwd = (EditText) findViewById(R.id.etPwd);
		button = (Button) findViewById(R.id.button);
	}
}

像所有的android通訊方法一樣,別忘了開啟internet的許可權~