android中Handler的初步認識(一)


說實話,之前在java開發的時候,很少涉及多線程的東西。由於開發的項目體量比較小,技術也比較差,所以更多的考慮的是功能,很少對併發做優化

 

如今藉着學習Android的機會,希望可以對多線程的知識有一個更好更全面地認識。哎,感覺自己技術基礎還是太差,好好加油吧

 

首先,安卓使用的時單線程模型:

    當一個程序第一次啓動時,Android會同時啓動一個對應的主線程(Main Thread),主線程主要負責處理與UI相關的事件,如:用戶的按鍵事件,用戶接觸屏幕的事件以及屏幕繪圖事件,並把相關的事件分發到對應的組件進行處理。所以主線程通常又被叫做UI線程。

    在開發Android應用時必須遵守單線程模型的原則: Android UI操作並不是線程安全的並且這些操作必須在UI線程中執行。

           如果在非UI線程中直接操作UI線程,會拋出android.view.ViewRoot$CalledFromWrongThreadException:Only the original thread that created a view hierarchy can touch itsviews,這與普通的java程序不同。

    由於UI線程負責事件的監聽和繪圖,因此,必須保證UI線程能夠隨時響應用戶的需求,UI線程裏的操作應該向中斷事件那樣短小,費時的操作(如網絡連接)需要另開線程,否則,如果UI線程超過5s沒有響應用戶請求,會彈出對話框提醒用戶終止應用程序。

    如果在新開的線程中需要對UI進行設定,就可能違反單線程模型,因此android採用一種複雜的Message Queue機制保證線程間通信。

    以上內容摘自http://www.cnblogs.com/nio-nio/archive/2012/07/23/2604900.html,是我在網上看到的相對比較容易理解的說明。讀完以上這幾段話之後,我對安卓單線程模式的理解:如果你要對UI進行操作那這些操作應該都放到主線程中;如果你要處理一些業務邏輯或者其它的比較費時的操作,那就把這些操作放到子線程中。那因爲大多數的UI更新都是基於業務邏輯的判斷,如何讓處理邏輯的子線程和處理UI的主線程通信呢,這就需要用到Handler


Handler是幹什麼的: 


        Handler可以分發Message對象和Runnable對象到主線程中,每個Handler實例,都會綁定到創建他的線程中(一般是位於主線程),它有兩個作用: (1): 安排消息或Runnable 在某個主線程中某個地方執行, (2)安排一個動作在不同的線程中執行。

關於Handler的使用,我們首先來看一個簡單例子:

        我們假設有這麼一個需求:我們需要在程序中點擊一個開始按鈕,然後我們的程序會在後臺啓動一個定時任務(每隔一秒打印一個語句)。另外,我們還需要一個取消按鈕,點擊它終止後臺的定時任務.

       我們先在界面中加入兩個按鈕,一個開始,一個取消。接下來是activity

 

package com.example.hander1;

import java.util.Date;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends Activity {

	Button startButton = null;
	Button stopButton = null;
	Handler handler = new Handler();
	
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		startButton = (Button) findViewById(R.id.startButton);
		stopButton = (Button) findViewById(R.id.stopButton);
		
		//爲button綁定onclicklistener
		startButton.setOnClickListener(new ButtonOnclikListener());
		stopButton.setOnClickListener(new ButtonOnclikListener());
	}
	
	Runnable going = new Runnable() {	
		@Override
		public void run() {
			//線程每次執行,打印日誌
			Log.i("run", "going  "+new Date().getSeconds());
			//延時一秒鐘後,再次將線程加入隊列中
			handler.postDelayed(going, 1000);
		}
	};

	class ButtonOnclikListener implements OnClickListener{

		@Override
		public void onClick(View v) {	
			switch(v.getId()){
			case R.id.startButton:
				begin();
				break;
			case R.id.stopButton:
				stop();
				break;
			default:
				break;
			}
		}
		
	}
	
	//該方法將going添加到message queue中 
	void begin(){
		handler.post(going);
	}
	
	//從message queue中移除going
	void stop(){
		handler.removeCallbacks(going);
	}

}

 

下面是運行結果,程序啓動後是這樣

      當我點擊啓動按鈕後,後臺每秒中打印一條語句

     當我點擊取消按鈕後,後臺不再打印語句

對於這個程序的運行過程我是這樣理解的:

      1、當我點擊啓動按鈕時:調用handler.post(going); 

handler的post方法說明是這樣的:Causes the Runnable r to be added to the message queue. The runnable will be run on the thread to which this handler is attached.

        也就是說,當我調用了handler.post(going);這個方法之後,handler會將我實現的Runnable對象加入到message queue中,系統會根據message queue來執行。後面一句很重要,runnable會在當前handler所屬的那個線程中執行,也就是說我的going會在程序的主線程中執行,而不是新開啓的其它線程。

·      2、在going的run方法中,我調用了handler.postDelayed(going, 1000);

     postDelayed這個方法和前面的post方法類似,只不過第二個參數的作用是讓我的going延遲一秒鐘執行。

     這樣其實就形成了一個死循環(我沒有規定跳出的條件),程序在我點擊啓動之後,每隔一秒鐘會在message queue中加入執行going的內容。

3、當我點擊取消按鈕時:調用handler.removeCallbacks(going);

      雖在我在上面的寫法中形成了一個死循環,但是我可以通過handler.removeCallbacks(going)這個方法,將message queue這個隊列中的going去掉,這樣就不再執行going了。而且根據這個方法源碼中的註釋:Remove any pending posts of Runnable r that are in the message queue. 應該是會把message queue中所有未執行going全部去掉。

這個程序雖然很簡單,但是我在一次看到的時候,仍然陷入了誤區。由於沒有仔細閱讀API,我剛開始以爲handler.post()這個方法會新開啓一個線程。後來看到API的時候才明白原來這個程序,只有一個主線程,只不過是通過handler將實現的Runnable 對象加入到message queue中執行的。


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