Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發 >> 關於android開發 >> Handler消息傳遞機制(一)

Handler消息傳遞機制(一)

編輯:關於android開發

Handler消息傳遞機制(一)


為什麼要用Handler:

出於性能優化考慮,Android的UI操作並不是線程安全的,這意味著如果有多個線程並發操作UI組件,可能導致線程安全問題。為了解決這個問題,Android制定了一條簡單的原則:只允許UI線程(亦即主線程)修改Activity中的UI組件。
當一個程序第一次啟動時,Android會同時啟動一條主線程,主線程主要負責處理與UI相關的事件,如用戶的按鍵事件、用戶接觸屏幕的事件、屏幕繪圖事件,並把相關的事件分發到相應的組件進行處理,所以主線程通常又叫做UI線程。

Handler的概念:

1)執行計劃任務,可以在預定的時間執行某些任務,可以模擬定時器
2)線程間通信。在Android的應用啟動時,會創建一個主線程,主線程會創建一個

消息隊列來處理各種消息。當你創建子線程時,你可以在你的子線程中拿到父線程中
創建的Handler 對象,就可以通過該對象向父線程的消息隊列發送消息了
。由於Android要求在UI線程中更新界面,因此,可以通過該方法在其它線程中更新界面。

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

? void handlerMessage(Message msg):處理消息的方法,該方法通常用於被重寫。
   ? final boolean hasMessage(int what):檢查消息隊列中是否包含what屬性為指定值的消息。
? sendEmptyMessage(int what):發送空消息
? final boolean sendMessage(Message msg):立即發送消息,注意這塊返回值,如果message成功的被放到message queue裡面則返回true,反之,返回false;(個人建議:對於這類問題不必主觀去記它,當實際使用時,直接查看源碼即可,源碼中有詳細的注釋)

Handler的作用:
(1)在一個線程中發送消息。
(2)在另一個線程中獲取、處理消息。
Handler處理的基本原理:

為了讓主線程能及時處理子線程發送的消息,顯然只能通過回調的方法來實現----開發者只要重寫Handler類中的方法,當新啟動的線程發送消息時,消息會發送至與之關聯的MessageQueue,而Handler會不斷的從MessageQuere中獲取並處理消息-----這將導致Handler類中處理消息的方法被回調。
在線程中使用Handler的基本步驟如下:
在被調用線程中完成以下內容:
(1)調用 Looper的prepare()方法為當前線程創建Looper對象,創建Looper對象時,它的構造器會創建與之配套的MessageQueue。
(2)有了Looper之後,創建Handler子類的實例,重寫HandlerMessage()方法,該方法負責處理來自其它線程的消息。
(3)調用Looper的loop()方法啟動Looper。

注:若被調用線程是主線程類,由於系統自動為主線程創建了Looper的實例,因此第一、三步驟可省略,而只需要做第2步即可。
在調用線程中完成:
(1)創建message,並填充內容。
(2)使用被調用類創建的Handler實例,調用sendMessage(Message msg)方法。

Handler 與線程的關系
Handler 一般運行於主線程內,也可以運行在子線程中,但是一定要創建Looper對象。主線程中Android已經為之創建了Looper對象

 

使用Handler的兩種常見情況:
1、只能在主UI中修改UI。但實際上,有部分UI需要在子線程中控制其修改邏輯,因此子線程需要通過handler通知主線程修改UI。這在游戲開發中尤其常見,比如需要讓新啟動的線程周期性的改變UI。、

子線程通知主UI線程修改UI組件的例子,新線程周期性的修改ImageView所顯示的圖片:

這個例子是Handler在主線程中獲取,處理消息,在子線程中發送消息

 

public class HandlerTest extends Activity
{
	// 定義周期性顯示的圖片的ID
	int[] imageIds = new int[]
	{
		R.drawable.java,
		R.drawable.ee,
		R.drawable.ajax,
		R.drawable.xml,
		R.drawable.classic
	};
	int currentImageId = 0;

	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		final ImageView show = (ImageView) findViewById(R.id.show);
		final Handler myHandler = new Handler()//在主線程中,獲取,處理消息,更新UI組件,可以修改UI組件
		{
			@Override
			public void handleMessage(Message msg)
			{
				// 如果該消息是本程序所發送的
				if (msg.what == 0x1233)
				{
					// 動態地修改所顯示的圖片
					show.setImageResource(imageIds[currentImageId++
						% imageIds.length]);
				}
			}
		};
		// 定義一個計時器,讓該計時器周期性地執行指定任務。子線程通知主線程修改UI組件,實現進程間通信
		new Timer().schedule(new TimerTask()
		{
			@Override
			public void run()
			{
				// 發送空消息
				myHandler.sendEmptyMessage(0x1233);在線程中發送消息
			}
		}, 0, 1200);
	}
}

2、為避免ANR,應該在子線程中執行耗時較長的操作,而此操作完成後,有可能需要通知主線程修改UI

 

在子線程中執行耗時任務後,通知主線程修改UI組件的例子:使用新進程計算質數,並用Toast顯示

這個例子是在主線程中發送消息,在子線程中獲取,處理消息

 

public class CalPrime extends Activity
{
	static final String UPPER_NUM = "upper";
	EditText etNum;
	CalThread calThread;
	// 定義一個線程類
	class CalThread extends Thread
	{
		public Handler mHandler;

		public void run()
		{
			Looper.prepare();//創建Looper對象,每個線程使用Handler時都要有一個Looper對象
			mHandler = new Handler()//在子線程中用handler獲取,處理消息
			{
				// 定義處理消息的方法
				@Override
				public void handleMessage(Message msg)
				{
					if(msg.what == 0x123)
					{
						int upper = msg.getData().getInt(UPPER_NUM);
						List<integer> nums = new ArrayList<integer>();
						// 計算從2開始、到upper的所有質數
						outer:
						for (int i = 2 ; i <= upper ; i++)
						{
							// 用i處於從2開始、到i的平方根的所有數
							for (int j = 2 ; j <= Math.sqrt(i) ; j++)
							{
								// 如果可以整除,表明這個數不是質數
								if(i != 2 && i % j == 0)
								{
									continue outer;
								}
							}
							nums.add(i);
						}
						// 使用Toast顯示統計出來的所有質數
						Toast.makeText(CalPrime.this , nums.toString()
							, Toast.LENGTH_LONG).show();
					}
				}
			};
			Looper.loop();//啟動Looper
		}
	}
	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		etNum = (EditText)findViewById(R.id.etNum);
		calThread = new CalThread();
		// 啟動新線程
		calThread.start();
	}
	// 為按鈕的點擊事件提供事件處理函數
	public void cal(View source)
	{
		// 創建消息
		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.mHandler.sendMessage(msg);//在主線程中發送消息
	}
}</integer></integer>

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved