Handler機制的原理 Android 的 Handler 機制(也有人叫消息機制)目的是為了跨線程通信,也就是多線程通信。之所以需 要跨線程通信是因為在 Android 中主線程通常只負責 UI 的創建和修改,子線程負責網路訪問和耗時操作, 因此,主線程和子線程需要經常配合使用才能完成整個 An ...
Handler機制的原理
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:
[html] view plain copy- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingBottom="@dimen/activity_vertical_margin"
- android:paddingLeft="@dimen/activity_horizontal_margin"
- android:paddingRight="@dimen/activity_horizontal_margin"
- android:paddingTop="@dimen/activity_vertical_margin"
- tools:context="com.seachal.htmlviewer.MainActivity"
- >
- <LinearLayout
- android:id="@+id/llay_top"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal" >
- <EditText
- android:id="@+id/et_url"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:hint="請輸入網路地址"
- android:text="http://www.baidu.com" />
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:onClick="load"
- android:text="確定" />
- </LinearLayout>
- <ScrollView
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_below="@id/llay_top"
- >
- <TextView
- android:id="@+id/tv_content"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/hello_world" />
- </ScrollView>
- </RelativeLayout>
工具類將位元組流轉化為字元串 StreamUtls.java:
[java] view plain copy- 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());
- }
- <span style="font-size:18px;"><strong>}</strong></span>
MainActivity.java
[java] view plain copy
- /**
- * 網路源碼查看器
- *
- * @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;
-