Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android的Handler機制

Android的Handler機制

編輯:關於Android編程

Android 的 Handler 機制(也有人叫消息機制)目的是為了跨線程通信,也就是多線程通信。之所以需要跨線程通信是因為在 Android 中主線程通常只負責 UI 的創建和修改,子線程負責網絡訪問和耗時操作,因此,主線程和子線程需要經常配合使用才能完成整個 Android 功能。

Handler 機制可以近似用圖 1 展示。MainThread 代表主線程,newThread 代表子線程。MainThread 是 Android 系統創建並維護的,創建的時候系統執行了 Looper.prepare();方法,該方法內部創建了 MessageQueue 消息隊列(也叫消息池),該消息隊列是 Message 消息的容器,用於存儲通過 handler發送過來的 Message。MessageQueue 是 Looper 對象的成員變量,Looper 對象通過 ThreadLocal 綁定在MainThread 中。因此我們可以簡單的這麼認為:MainThread 擁有唯一的一個 Looper 對象,該 Looper 對象有用唯一的 MessageQueue 對象,MessageQueue 對象可以存儲多個 Message。

MainThread 中需要程序員手動創建 Handler 對象,並覆寫 Handler 中的 handleMessage(Message msg)方法,該方法將來會在主線程中被調用,在該方法裡一般會寫與 UI 修改相關的代碼。

MainThread 創建好之後,系統自動執行了 Looper.loop();方法,該方法內部開啟了一個“死循環”不斷的去之前創建好的 MessageQueue 中取 Message。如果一有消息進入 MessageQueue,那麼馬上會被

Looper.loop();取出來,取出來之後就會調用之前創建好的 handler 對象的 handleMessage(Message)方法。

newThread 線程是我們程序員自定 new 出來的子線程。在該子線程中處理完我們的“耗時”或者網絡訪問任務後,調用主線程中的 handler 對象的 sendMessage(msg)方法,該方法一被執行,內部將就 msg添加到了主線程中的 MessageQueue 隊列中,這樣就成為了 Looper.loop()的盤中餐了,等待著被消費。這是一個很復雜的過程,但是 Android 顯然已經將這種模式給封裝起來了,就叫 Handler 機制。我們使用時只需要在主線程中創建 Handler,並覆寫 handler 中的handleMessage 方法,然後在子線程中調用 handler 的 sendMessage(msg)方法即可。

\

圖1 Handler原理圖

 

案例

網頁源碼查看器:

activity_layout.xml:

 



    

        
工具類將字節流轉化為字符串 StreamUtls.java:
public class StreamUtils {
	/**
	 * 將字節流轉化為字符串,使用android 默認編碼
	 * 
	 * @author ZhangSeachal
	 * @date 2016年8月6日下午4:20:43
	 * @version 1.0
	 * @param inputStream
	 * @return
	 * @throws IOException
	 */
	public static String inputStream2String(InputStream inputStream)
			throws IOException {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		int len = -1;
		byte[] buffer = new byte[1024];
		while ((len = inputStream.read(buffer)) != -1) {
			baos.write(buffer, 0, len);
		}
		inputStream.close();
		return new String(baos.toByteArray());

	}

}

 

MainActivity.java

 

/**
 * 網絡源碼查看器
 * 
 * @author ZhangSeachal
 * @date 2016年8月5日 下午10:07:34
 * @version 1.0
 * @since
 */
public class MainActivity extends Activity {
	private TextView tv_content;
	private EditText et_url;

	/** 創建一個Handler對象, 覆寫類體、方法體 */
	private Handler handler = new Handler() {
		/**
		 * 覆寫handleMessage方法,在該方法中完成我們想做的工作, 該方法是在主線程中 被 調用的,因此可以再這裡面修改UI。
		 */
		public void handleMessage(Message msg) {
			// 判斷Message 的類型,根據msg的what屬性去獲取期類型
			switch (msg.what) {
			// 如果成功
			case RESULT_OK:
				// 從msg的obj屬性中獲取數據,然後顯示在TextView 上。
				tv_content.setText(msg.obj.toString());
				break;
			// 如果失敗
			case RESULT_CANCELED:
				// 彈 吐司,給用戶提示
				Toast.makeText(MainActivity.this, "訪問網頁失敗", Toast.LENGTH_LONG)
						.show();
			default:
				break;
			}
		}
	};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		// 初始化控件
		et_url = (EditText) findViewById(R.id.et_url);
		tv_content = (TextView) findViewById(R.id.tv_content);

	}

	/**
	 * 加載 網頁源碼
	 * 
	 * @author ZhangSeachal
	 * @date 2016年8月5日下午10:29:12
	 * @version 1.0
	 * @param view
	 */
	public void load(View view) {
		// 獲取用戶輸入的數據
		final String path = et_url.getText().toString().trim();
		/*
		 * 網絡訪問必須在子線程中進行
		 */
		new Thread(new Runnable() {

			@Override
			public void run() {
				// TODO Auto-generated method stub
				try {
					// 1.創建一個 URl對象,需要傳入url
					URL url = new URL(path);
					/*
					 * 2.使用url對象打開一個HttpURLConnection,
					 * 由於其返回的是HttpURLConnection的父類,
					 */
					HttpURLConnection connection = (HttpURLConnection) url
							.openConnection();
					/*
					 * 3.配置connection 連接參數
					 */
					// 設置聯網超時時長,單位毫秒
					connection.setConnectTimeout(5000);
					/*
					 * 設置數據讀取超時 注意: 不是指讀取數據總耗時超時, 而是能夠讀取到數據流等待時長
					 */
					connection.setReadTimeout(5000);
					/**
					 * 設置請求方式,默認是GET,但是為了增加代碼易讀性, 建議顯示只是為GET
					 */
					connection.setRequestMethod("GET");
					// 4. 開始連接網絡
					connection.connect();
					// 5.以字節 輸入流 的形式獲取服務端發來的數據
					InputStream inputStream = connection.getInputStream();
					// 6.將字節流轉化為字符串 (使用自定義的StreamUtils工具類)
					final String data = StreamUtils
							.inputStream2String(inputStream);
					/*
					 * 7.將獲取的數據封裝到Message對象,然後發送給handler
					 */
					Message msg = new Message();
					/*
					 * 給Message 對象 的what屬性設置一個int類型的值。 因為消息可能會有多個,因此為了區分這些不同的消息。
					 * 需要給消息設置What屬性. RESULT_OK 是Activity的常量值為-1,
					 * 當然也可以自定義一個int類型的值。
					 */
					msg.what = RESULT_OK;
					// msg.what = RESULT_CANCELED;
					/**
					 * 給Message隊形的obj屬性設置一個object類型的屬性。 該值正是我們需要在
					 * Meaage對象上綁定的數據,這裡綁定的 從網絡上獲取到的網頁編碼字符串。
					 */
					msg.obj = data;
					/*
					 * 給主線程發送消息。 發送後,系統會調用handler對象的handlerMessage(Message) 方法。
					 * 該方法正是 我們自己實現的,而且該方法是在主線程中執行的。 從而就實現了從子線程中
					 * 訪問網絡數據(耗時操作),然後交給主線程, 讓主線程修改UI(修改UI只能在主線程中做)。
					 */
					handler.sendMessage(msg);
				} catch (Exception e) {
					// TODO: handle exception
					e.printStackTrace();
					Log.d("tag", "遇到異常" + e, e);
					/**
					 * 如果遇到異常,最好讓主線程也知道子線程遇到異常了。 因此使用handler 發動一個空消息,
					 * 所謂的空消息是指,該消息沒有obj值, 只有一個what屬性。 這列的RESULT_CANCELED
					 * 就是一個int型的常量, 當然我們可以自定義,這裡只不過是直接使用了Activity類的 一個常量而已。
					 * 該消息發送後,系統依然會調用handler對象 的handlerMessage(Message)方法。
					 */
					handler.sendEmptyMessage(RESULT_CANCELED);
				}
			}
		}).start();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		// Handle action bar item clicks here. The action bar will
		// automatically handle clicks on the Home/Up button, so long
		// as you specify a parent activity in AndroidManifest.xml.
		int id = item.getItemId();
		if (id == R.id.action_settings) {
			return true;
		}
		return super.onOptionsItemSelected(item);
	}
}

最後在AndroidManifest.xml 中添加網絡訪問的權限

 

 




    
   
    
        
            
                

                
            
        
    


然後就大功告成了,運行一下去看看效果吧。如果有用就收藏一下吧!
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved