Handler消息處理機制

  Android在設計時引入了Handler消息機制,每個消息發送到主線路的消息隊列中,消息隊列遵循先進先出原則,發送消息不會阻塞線程,而接收線程會阻塞線程。Handler容許發送並處理Message消息,Message對象經過主線程的MessageQueue消息隊列相關聯的Message和Runnable對象進行存取。每一個Handler實例對Message消息發送和接收與對應主線程和主線程的消息隊列有關。當建立一個新的Handler時,Handler就屬於當前主線程,主線程MessageQueue消息隊列也同步建立,即Handler會綁定到建立該Handler的主線程/消息隊列上。而後,Handler就能夠經過主線程的消息隊列發送和接收Message消息對象了。java

Handler的特性android

1)Android裏沒有全局Message Queue消息隊列,每一個Activity主線程都有一個獨立的Message Queue消息隊列,消息隊列採用先進先出原則。不一樣APK應用不能經過Handler進行Message通訊,同一個APK應用中能夠經過Handler對象傳遞而進行Message通訊。app

2)每一個Handler實例都會綁定到建立它的線程中(通常位於主線程,即Activity線程),可是Handler實例能夠在任意線程中建立(能夠在主線程或子線程中建立)異步

3)Handler發送消息使用Message Queue消息隊列,每一個Message發送到消息隊列裏面;發送消息採用異步方式,因此不會阻塞線程。而接收線程則採用同步方式,因此會阻塞線程,因此當Handler處理完一個Message對象後纔會去取下一下消息進行處理。ide

  下面咱們經過一個實例來加深一下對以上內容的理解,在這個實現中,經過四個Button的onClick事件,實現不一樣方式的Handler消息處理機制。函數

button_1:完成在主線程中傳遞Handler消息處理oop

button_2:在子線程中完成Handler消息處理this

button_3:在其它線程完傳入Handler對象並完成其消息處理spa

button_4:一個實驗,驗證在其它線程中更新ActivityUI時會拋出異常(這個我沒實現,感興趣的本身寫吧).net

話很少說,直接上源碼吧

1)activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    <Button
        android:id="@+id/button_1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="handle msg in main thread"/>
    <Button
        android:id="@+id/button_2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="handle msg in sub thread"/>
    <Button
        android:id="@+id/button_3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="handler comes from other thread"/>
    <Button
        android:id="@+id/button_4"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="refresh ActivityUI in other thread"/>
</LinearLayout>

2)MainActivity.java

package cn.lion.handlertest;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends Activity implements OnClickListener{
 private Button button1 = null;
 private Button button2 = null;
 private Button button3 = null;
 //private Button button4 = null;
 private MyHandler mHandler = null;
 private Message msg = null;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  button1 = (Button)findViewById(R.id.button_1);
  button2 = (Button)findViewById(R.id.button_2);
  button3 = (Button)findViewById(R.id.button_3);
  //button4 = (Button)findViewById(R.id.button_4);
  button1.setOnClickListener(this);
  button2.setOnClickListener(this);
  button3.setOnClickListener(this);
  //button4.setOnClickListener(this);
 }
 @Override
 public void onClick(View v) {
  dealOnClick(v.getId());  
 }
 /**
  * 自定義一個處理onClick事件的方法
  * @param msgId
  */
 private void dealOnClick(int msgId) {
  mHandler = new MyHandler(); //子線程和主線程均可以操做這個mHandler,固然也能夠在switch的具體case中才建立
  
  switch(msgId){
  case R.id.button_1:
   // 主線程中建立MyHandler對象,mHandler建立Handler消息類型爲一、消息對象爲字符串的Message對象
   //mHandler = new MyHandler();
   msg = mHandler.obtainMessage(1, (Object)"Main thread send message by Message Object");
   msg.sendToTarget(); //Handler 發送消息
   break;
  case R.id.button_2:
   InnerThread innerThread = new InnerThread();
   innerThread.start(); //內部線程異步處理button_2的事件
   break;
  case R.id.button_3:
   //mHandler = new MyHandler();
   SubThread subThread = new SubThread(mHandler); //把主線程中建立的mHandler對象傳遞給子線程
   subThread.start(); //子線程對象,用於異步處理Handler Message消息
   break;   
  }  
 }
 
 /**
  * 自定義MyHandler,繼承自Handler,覆蓋父類的 handleMessage() 方法,msg.obj
  * 是Message對象傳遞過來的Object對象,本例中爲String對象 。
  * @author lion
  *
  */
 public class MyHandler extends Handler{
  public MyHandler(Looper myLooper){
   super(myLooper);  //重寫構造方法,經過Looper建立MyHandler對象
  }
  public MyHandler(){
   
  }
  @Override
  public void handleMessage(Message msg){
   String str = "";
   switch(msg.what){
   case 1:
    str = "1: " + msg.obj;
    break;
   case 2:
    str = "2: " + msg.obj;
    break;
   case 3:
    str = "3: " + msg.obj;
    break;
   }
   Toast toast = Toast.makeText(MainActivity.this, str, Toast.LENGTH_SHORT);
   toast.show();
  }
 }
 
 private class InnerThread extends Thread{
  @Override
  public void run(){
   
   //只能在Activity的主線程中使用不帶Looper對象的構造函數來建立Handler對象,因此此行會拋出異常
   //mHandler = new MyHandler();
   
   //Lopper.myLooper()獲取Looper爲null,因此此行會拋出異常
   //mHandler = new MyHandler(Looper.myLooper());
   
   //經過Looper.getMainLooper()獲取父類的looper能夠成功建立Handler對象並將Message了送到父類
   mHandler = new MyHandler(Looper.getMainLooper());
   
   Message msg = mHandler.obtainMessage(2, (Object)"Inner thread send message by Message Object");
   msg.sendToTarget();
  }
 }
 
 /**
  * 子線程經過Handler對象建立Message對象,並在子線程中發送Handler消息,這個消息將由主線程在handleMessage中接收並響應
  * @author lion
  *
  */
 public class SubThread extends Thread{
  private Handler mHandler;
  public SubThread(Handler mHandler){
   this.mHandler = mHandler;
  }
  @Override
  public void run(){
   Message msg = mHandler.obtainMessage(3, (Object)"Other thread send message by myHandler");
   msg.sendToTarget();
  }
 }
}

  仔細分析一下Handler的做用:經過上面的實例能夠看出,Handler的主要做用是異步處理較費時的操做,優先將界面返回給用戶,異步處理完成後再去更新用戶界面。在Android中啓動應用程序時,Android首先會開啓一個主線程(即UI線程),主線程專門管理界面中的UI控件,對事件進行分發,如點擊一個Button,Android會分發事件到具體的Button上,以響應用戶的操做。

  若是須要處理一個耗時的操做,以下載文件,那麼這種操做就絕對不應放到主線程中進行,不然界面會出現假死現象。咱們要把這種耗時的操做放到子線程中去處理,子線程處理完成後再根據處理結果決定是否通知主線程更新UI,注意在子線程中是沒法更新主線程的界面(UI)的。爲了解決這個問題,Android引入了Handler,因爲Handler運行在主線程中(即運行於Activity UI線程中),它與子線程能夠經過Message對象來傳遞數據。因此能夠利用Handler,從子線程中向主線程發送更新UI的消息,從而實現了UI的及時更新。

至於主線程和子線程如何通訊、Handler如何獲取Message等問題,見我上篇文章 android的消息處理機制——Looper,Handler,Message

相關文章
相關標籤/搜索