Handler的用法

首先撇清一個概念:Android的單線程設計是指每個應用程序的UI線程(主線程)是單線程的,即和用戶交互的界面是單線程的。但是,很顯然,用戶界面如果採用多線程處理效率會更高,Android爲什麼將UI線程限制爲單線程呢?這是爲了避免併發編程的複雜性,也是提高Android應用的健壯性的有效途徑。

但是,主線程是單線程的,並不等於Android不支持多線程,比如兩個Android應用程序之間的通訊。

Android中的Handler提供了線程之間通訊的簡便手段,如下圖所示:

handler

 

Android的主線程(即UI線程)內部實現了消息隊列機制,即一個主線程會自動創建一個消息隊列(顯然也只有一個消息隊列),同時創建一個讀取消息隊列的Looper對象。主線程的所有用戶交互都通過消息隊列和Looper對象來實現:當有新的消息進入消息隊列時,Looper會及時讀取消息並調用相應的處理邏輯(更新用戶界面)。

當其他線程希望更新用戶界面時,可以通過Handler提供的sendMessage方法在消息隊列中放入一個消息(Message對象,可以攜帶大量信息),這樣Looper即可以及時獲取該消息,並調用Handler相應的handleMessage方法處理該消息,這樣就實現了線程之間的通訊。由此可以看出,Handler在線程通訊中起到了一個樞紐作用:Handler即負責和消息隊列打交道,也負責處理相應的消息,其他線程通過Handler和主線程通訊,就可以不需要考慮和主線程的競爭和同步問題,極大的簡化了線程的使用。

下面的示例代碼演示了一個每隔1秒顯示一條消息在主界面:

 

public class MainActivity extends Activity {
	private TextView label;
	private static int UPDATE = 1;
	private Handler handler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			if (msg.what == 1) {
				label.setText(String.valueOf(msg.obj));
				super.handleMessage(msg);
			}
		}
	};
 
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		label = (TextView) findViewById(R.id.label);
 
		new Thread() {
			@Override
			public void run() {
				for (int i = 0; i < 100; i++) {
 
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
 
					Message msg = new Message();
					msg.what = UPDATE;
					msg.obj = "當前循環變量:" + i;
					handler.sendMessage(msg);
				}
			}
 
		}.start();
	}
 
}

在上面的例子中,所謂的其他線程其實主線程的子線程,下面一個例子則演示了完全不同的兩個組件通過Handler實現的交互:一個Service每隔1秒產生一個隨機數,然後在主線程顯示出來:

ServiceDemo.java文件:

public class ServiceDemo extends Service {
 
	@Override
	public IBinder onBind(Intent arg0) {
		// TODO Auto-generated method stub
		return null;
	}
 
	private Thread workThread;
 
	@Override
	public void onCreate() {
	    super.onCreate();
	    Toast.makeText(this, "(1) onCreate()", 
	    		Toast.LENGTH_LONG).show();    
	    workThread = new Thread(null,backgroudWork,"WorkThread");
	}
 
	@Override
	public void onStart(Intent intent, int startId) {
	      super.onStart(intent, startId);
	      Toast.makeText(this, "(2) onStart()", 
	    		  Toast.LENGTH_SHORT).show();
	      if (!workThread.isAlive()){
	    	  workThread.start();
	      }
 
	}
 
	@Override
	public void onDestroy() {
	     super.onDestroy();
	     Toast.makeText(this, "(3) onDestroy()", 
	    		 Toast.LENGTH_SHORT).show();     
	     workThread.interrupt();
	}
 
	private Runnable backgroudWork = new Runnable(){
		@Override
		public void run() {
			try {
				while(!Thread.interrupted()){				
					double randomDouble = Math.random();
					MainActivity.updateGUI(randomDouble);
					Thread.sleep(1000);
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	};
 
}

MainActivity.java文件:

public class MainActivity extends Activity {
	private static TextView label;
	private static int UPDATE = 1;
	protected static Handler handler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			if (msg.what == 1) {
				label.setText(String.valueOf(msg.obj));
				super.handleMessage(msg);
			}
		}
	};
 
	public static void updateGUI(double doubleRadom){
		Message msg = new Message();
		msg.what = UPDATE;
		msg.obj = "當前隨機數:" + doubleRadom;
		handler.sendMessage(msg);
	}
 
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		label = (TextView) findViewById(R.id.label);
 
		final Intent serviceIntent = new Intent(this, ServiceDemo.class);
		startService(serviceIntent);
	}
 
}

可以看出,爲了實現在兩個組件間的通訊,主要的變化是:

  • 將Handler對象定義爲static類型的,便於在其他組件訪問handler
  • 在主線程中實現一個靜態的updateGUI方法,以便通過調用handler.sendMessage添加一個消息到消息隊列中
  • 在Service的線程中,調用主線程的updateGUI並傳入適當的參數

 

Handler和消息循環機制的設計很巧妙,在WEB應用和企業級應用中是否可以借鑑呢?

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