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

Android的Handler機制,androidhandler

編輯:關於android開發

Android的Handler機制,androidhandler


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 ><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;  
  •             }  
  •         }  
  •     };  
  •   
  •     @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 中添加網絡訪問的權限

    [html] view plain copy
  • <?xml version="1.0" encoding="utf-8"?>  
  • <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
  •     package="com.seachal.htmlviewer"  
  •     android:versionCode="1"  
  •     android:versionName="1.0" >  
  •   
  •     <uses-sdk  
  •         android:minSdkVersion="16"  
  •         android:targetSdkVersion="19" />  
  •    <uses-permission  android:name="android.permission.INTERNET"/>  
  •     <application  
  •         android:allowBackup="true"  
  •         android:icon="@drawable/ic_launcher"  
  •         android:label="@string/app_name"  
  •         android:theme="@style/AppTheme" >  
  •         <activity  
  •             android:name=".MainActivity"  
  •             android:label="@string/app_name" >  
  •             <intent-filter>  
  •                 <action android:name="android.intent.action.MAIN" />  
  •   
  •                 <category android:name="android.intent.category.LAUNCHER" />  
  •             </intent-filter>  
  •         </activity>  
  •     </application>  
  •   
  • </manifest>  

  • 然後就大功告成了,運行一下去看看效果吧。如果有用就收藏一下吧!

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