android學習之——Handler消息傳遞機制

Android的消息傳遞機制是另一種形式的“事件處理”,這種機制主要是爲了解決Android應用的多線程問題——Android平臺只允許UI線程修改Activity裏的組件,這樣就會導致新啓動的線程無法動態改變界面組件的屬性值。但實際開發中,需要讓新的線程能夠改變界面組件的屬性值,這就需要藉助於Handler的消息傳遞機制來實現了。


Handler類簡介:

Handler類主要作用:

  • 在新啓動的線程中發送消息
  • 在主線程中獲取、處理消息

通過回調的方法——重寫Handler類中處理消息的方法來讓主線程能適時地出口i新啓動線程所發送的消息。,當新啓動的線程發送消息時,消息會發送到與之關聯的MessageQueue,而Handler會不斷地從MessageQueue中獲取並處理消息——這將導致Handler類中處理消息的方法被回調。

Handler類中包含如下用於發送、處理消息的方法:

  • void handleMessage(Message msg):處理消息的方法。通常被重寫。
  • final boolean hasMessages(int what):檢查消息隊列中是否包含what屬性爲指定值的消息。
  • final boolean hasMessages(int what,Object object):檢查消息隊列中是否包含what屬性爲指定值且object屬性爲指定對象的消息。
  • 多個重載的Message obtainMessage():獲取消息。
  • sendEmptyMessage(int what):發送空消息。
  • final boolean sendEmptyMessageDelayed(int what,long delayMillis):指定多少毫秒後發送空消息。
  • final boolean sendMessage(Message msg ):立即發送消息。
  • final boolean sendMessageDelayed(Message msg,long delayMillis):指定多少毫秒後發送消息。

下面的一個小例子可以通過新線程來週期性地改變ImageView所顯示的圖片。
public class MainActivity extends Activity {
	
	//定義週期性改變的圖片的ID
	int[] imagesId = new int[]{
		R.drawable.a,
		R.drawable.b,
		R.drawable.c,
		R.drawable.d,
		R.drawable.e
	};
	int currentImageId = 0;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		final ImageView show = (ImageView) findViewById(R.id.show);
		
		final Handler mHandler = new Handler(){

			@Override
			public void handleMessage(Message msg) {
				// TODO Auto-generated method stub
				//判斷該消息是否是本程序發送的
				if(msg.what == 0x123){
					//動態地改變顯示的圖片的ID
					show.setImageResource(imagesId[currentImageId++]);
					if(currentImageId>4){
						currentImageId = 0;
					}
				}
			}
			
		};
		//定義一個計時器,讓該計時器週期性地執行指定任務
		new Timer().schedule(new TimerTask() {
			
			@Override
			public void run() {
				// TODO Auto-generated method stub
				//新啓動的線程無法訪問Activity裏的組件
				//所以需要Handler發送消息
				Message msg = new Message();
				msg.what = 0x123;
				
				//發送消息
				mHandler.sendMessage(msg);
			}
		}, 0, 800);
	}
}
當新線程發送消息時,Handler 的handleMessage(Message msg)方法被自動回調,來改變Activity中組件的屬性。

與Handler一起工作的幾個組件(爲了更好地理解Handler的工作原理):

  • Handler:把消息發送給Looper管理的MessageQueue,並負責處理Lopper分給它的消息。
  • Message:Handler接收和處理的消息對象。
  • Looper:每個線程只能有一個Looper。它的loop方法負責讀取MessageQueue中的消息,並將消息分給對應的Handler處理。
  • MessageQueue:消息隊列,先進先出方式管理Message,程序創建Looper對象時會在它的構造器中創建MessageQueue對象。源碼如下:
private Looper(){
	mQueue = new MessageQueue();
	mRn = true;
	mThread = Thread.currentThread();
}
通過源碼可知,無法通過構造器來創建Looper對象,程序在初始化Looper時會創建一個與之關聯的MessageQueue,這個MessageQueue負責管理消息。

如果要Handler正常工作,就要在當前線程中必須有一個Looper對象:

  • 主UI線程中,系統已經初始化了一個Looper對象,因此程序直接創建Handler即可,然後就可通過Handler來發送消息、處理消息。
  • 我們自己啓動的子線程,必須自己創建一個Looper對象,並啓動它。創建Looper對象調用它的prepare()方法即可(prepare()方法保證了每一個線程最多隻有一個Looper對象),然後調用Looper靜態loop()方法來啓動它(loop()使用死循環,不斷從MessageQueue中取出消息,傳遞給對應的Handler處理)。
在線程中使用Handler的步驟:

  1. 調用Looper的prepare()方法創建Looper對象,同時會有與之匹配的MessageQueue被創建
  2. 創建Handler子類實例,重寫handlerMessage()方法,負責處理來自其他線程的消息。
  3. 調用Looper的loop()方法啓動Looper。

例:利用新線程計算質數

public class MainActivity extends Activity {
	
	//2~UPPER_NUM範圍內的質數
	static final String UPPER_NUM = "upper";
	EditText etNum;
	
	//定義一個線程類用於計算質數
	CalThread calThread;
	class CalThread extends Thread{
		public Handler handler;
		public void run(){
			//Looper的prepare方法用於創建Looper對象,同時創建與之對應的MessageQueue
			Looper.prepare();
			handler = new Handler(){
				//重寫處理消息的方法
				@Override
				public void handleMessage(Message msg) {
					// TODO Auto-generated method stub
					if(msg.what==0x123){
						//獲取從2到upper的質數
						int upper = msg.getData().getInt(UPPER_NUM);
						List<Integer> nums = new ArrayList<Integer>();
						outer:
							for(int i = 2;i<upper;i++){
								for(int j = 2;j<Math.sqrt(i);j++){
									if(i!=2&&i%j==0){
										continue outer;
									}
								}
								nums.add(i);
							}
						//把得到的質數顯示出來
						Toast.makeText(MainActivity.this, nums.toString(),Toast.LENGTH_LONG).show();
					}
				}
				
			};
			//啓動Looper
			Looper.loop();
		}
	}
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		etNum = (EditText) findViewById(R.id.etNum);
		calThread = new CalThread();
		calThread.start();
	}
	
	//
	public void cal(View v){
		//創建消息
		Message msg = new Message();
		msg.what = 0x123;
		
		Bundle bundle = new Bundle();
		bundle.putInt(UPPER_NUM, Integer.parseInt(etNum.getText().toString()));
		msg.setData(bundle);
		
		//向新線程中的Handler發送消息
		calThread.handler.sendMessage(msg);
	}


}
運行該程序,根據輸入的數據計算該範圍內的質數都會交給新線程,前臺UI不受影響。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章