Handler初探

    Android中有着和J2SE同樣優秀的多線程支持,可以把那些耗時較多的操作放在新線程中操作。但是當新線程中有涉及到操作UI的操作時,就會對主線程產生危險,因此,Android提供了Handler作爲主線程和子線程的紐帶。同時,Handler對象初始化後,就默認與對它初始化的進程的消息隊列綁定,因此可以利用Handler所包含的消息隊列,制定一些操作的順序。

   根據SDK文檔的說明:“A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue.”由此可以清楚的知道,可以把消息和Runnble對象發送到與Handler對象所關聯的消息隊列中去。

    

     下面,就Handler的以上兩點作用,分別進行討論。

    1. 傳遞Message。用於接受子線程發送的數據, 並用此數據配合主線程更新UI。

    在Android中,對於UI的操作通常需要放在主線程中進行操作。如果在子線程中有關於UI的操作,那麼就需要把數據消息作爲一個Message對象發送到消息隊列中,然後,有Handler中的handlerMessge方法處理傳過來的數據信息,並操作UI。當然,Handler對象是在主線程中初始化的,以爲它需要綁定在主線程的消息隊列中。

    類sendMessage(Message msg)方法實現發送消息的操作。 在初始化Handler對象時重寫的handleMessage方法來接收Messgae並進行相關操作。

  

 
 

  1. //Handler處理子線程消息代碼示例:  
  2.  
  3. public class Activity01 extends Activity  
  4. {  
  5.     //聲明ProgressBar對象  
  6.     private ProgressBar m_ProgressBar;  
  7.     private ProgressBar m_ProgressBar2;  
  8.     private Button mButton01;  
  9.     protected static final int GUI_STOP_NOTIFIER = 0x108;  
  10.     protected static final int GUI_THREADING_NOTIFIER = 0x109;  
  11.     public int intCounter=0;  
  12.     /** Called when the activity is first created. */ 
  13.     @Override 
  14.     public void onCreate(Bundle savedInstanceState)  
  15.     {  
  16.         super.onCreate(savedInstanceState);  
  17.         //設置窗口模式,,因爲需要顯示進度條在標題欄  
  18.         requestWindowFeature(Window.FEATURE_PROGRESS);  
  19.         setProgressBarVisibility(true);  
  20.         setContentView(R.layout.main);  
  21.           
  22.         //取得ProgressBar  
  23.         m_ProgressBar = (ProgressBar) findViewById(R.id.ProgressBar01);  
  24.         m_ProgressBar2= (ProgressBar) findViewById(R.id.ProgressBar02);  
  25.         mButton01 = (Button)findViewById(R.id.Button01);   
  26.           
  27.         m_ProgressBar.setIndeterminate(false);  
  28.         m_ProgressBar2.setIndeterminate(false);  
  29.           
  30.         //當按鈕按下時開始執行,  
  31.         mButton01.setOnClickListener(new Button.OnClickListener()  
  32.         {  
  33.           @Override 
  34.           public void onClick(View v)  
  35.           {  
  36.             // TODO Auto-generated method stub  
  37.                 
  38.               //設置ProgressBar爲可見狀態  
  39.               m_ProgressBar.setVisibility(View.VISIBLE);  
  40.               m_ProgressBar2.setVisibility(View.VISIBLE);  
  41.               //設置ProgressBar的最大值  
  42.               m_ProgressBar.setMax(100);  
  43.               //設置ProgressBar當前值  
  44.               m_ProgressBar.setProgress(0);  
  45.               m_ProgressBar2.setProgress(0);  
  46.  
  47.               //通過線程來改變ProgressBar的值  
  48.   new Thread(new Runnable() {  
  49.      public void run()  
  50.         {  
  51.      for (int i = 0; i < 10; i++)  
  52.         {  
  53.            try 
  54.            {  
  55.                  intCounter = (i + 1) * 20;  
  56.                  Thread.sleep(1000);  
  57.  
  58.             if (i == 4)  
  59.             {  
  60.             Message m = new Message();  
  61.  
  62.              m.what = Activity01.GUI_STOP_NOTIFIER;  
  63.             Activity01.this.myMessageHandler.sendMessage(m);
  64. //將message發送到消息隊列  
  65.               break;  
  66.              }  
  67.             else 
  68.            {  
  69.            Message m = new Message();  
  70.            m.what = Activity01.GUI_THREADING_NOTIFIER;  
  71.            Activity01.this.myMessageHandler.sendMessage(m);  
  72. //將message發送到消息隊列  
  73.                                 }  
  74.                             }  
  75.                             catch (Exception e)  
  76.                             {  
  77.                                 e.printStackTrace();  
  78.                             }  
  79.                         }  
  80.                     }  
  81.                 }).start();  
  82.             }  
  83.         });  
  84.     }  
  85.  
  86. //通過匿名類複寫Handler類中的handleMessage方法,用於接收傳遞到消息隊列中的Message,並進行UI操作。
  87.       Handler myMessageHandler = new Handler()  
  88.       {  
  89.         // @Override   
  90.           public void handleMessage(Message msg)  
  91.           {  
  92.               switch (msg.what)  
  93.               {  
  94.               //ProgressBar已經是對大值  
  95.               case Activity01.GUI_STOP_NOTIFIER:  
  96.                   m_ProgressBar.setVisibility(View.GONE);  
  97.                   m_ProgressBar2.setVisibility(View.GONE);  
  98.                   Thread.currentThread().interrupt();  
  99.                   break;  
  100.               case Activity01.GUI_THREADING_NOTIFIER:  
  101.                   if (!Thread.currentThread().isInterrupted())  
  102.                   {  
  103.                       // 改變ProgressBar的當前值  
  104.                       m_ProgressBar.setProgress(intCounter);  
  105.                       m_ProgressBar2.setProgress(intCounter);  
  106.                         
  107.                       // 設置標題欄中前景的一個進度條進度值  
  108.                       setProgress(intCounter*100);  
  109.                       // 設置標題欄中後面的一個進度條進度值  
  110.                       setSecondaryProgress(intCounter*100);//  
  111.                   }  
  112.                   break;  
  113.               }  
  114.               super.handleMessage(msg);  
  115.          }  
  116.       };  
  117. }  

    以上的例子中,子線程只是對進度條的參數進行了變更,並將結果以message形式發送到消息隊列中去,子線程的內部並未進行UI操作,而是在重寫的Handler的handlerMessage方法中操作了UI界面。

    2. 傳遞Runnable對象。用於通過Handler綁定的消息隊列,安排不同操作的執行順序。

    Handler對象在進行初始化的時候,會默認的自動綁定消息隊列。利用類post方法,可以將Runnable對象發送到消息隊列中,按照隊列的機制按順序執行不同的Runnable對象中的run方法。

   

 

  1. public class HandlerActivity extends Activity {  
  2.     /** Called when the activity is first created. */ 
  3.     //聲明兩個按鈕控件  
  4.     private Button startButton = null;  
  5.     private Button endButton = null;  
  6.     @Override 
  7.     public void onCreate(Bundle savedInstanceState) {  
  8.         super.onCreate(savedInstanceState);  
  9.         setContentView(R.layout.main);  
  10.         //根據控件的ID得到代表控件的對象,並未這兩個按鈕設置相應的監聽器  
  11.         startButton = (Button)findViewById(R.id.startButton);  
  12.         startButton.setOnClickListener(new StartButtonListener());  
  13.         endButton = (Button)findViewById(R.id.endButton);  
  14.         endButton.setOnClickListener(new EndButtonListener());  
  15.           
  16.     }  
  17.     class StartButtonListener implements OnClickListener{  
  18.  
  19.         @Override 
  20.         public void onClick(View v) {  
  21.             //調用Handler的post方法,將要執行的線程對象添加到隊列當中  
  22.             handler.post(updateThread);  
  23.         }  
  24.           
  25.     }  
  26.       
  27.     class EndButtonListener implements OnClickListener{  
  28.  
  29.         @Override 
  30.         public void onClick(View v) {  
  31.             handler.removeCallbacks(updateThread);  
  32.         }  
  33.           
  34.     }  
  35.     //創建一個Handler對象  
  36.     Handler handler  = new Handler();  
  37.     //將要執行的操作寫在線程對象的run方法當中  
  38.     Runnable updateThread =  new Runnable(){  
  39.  
  40.         @Override 
  41.         public void run() {  
  42.             System.out.println("UpdateThread");  
  43.             //在run方法內部,執行postDelayed或者是post方法  
  44.             handler.postDelayed(updateThread, 3000);  
  45.         }  
  46.           
  47.     };  

    程序的運行結果就是每隔3秒鐘,就會在控制檯打印一行UpdateTread。這是因爲實現了Runnable接口的updateThread對象進入了空的消息隊列即被立即執行run方法,而在run方法的內部,又在3000ms之後將其再次發送進入消息隊列中。

   3. Handler和多線程

    post方法雖然發送的是一個實現了Runnable接口的類對象,但是它並非創建了一個新線程,而是執行了該對象中的run方法。也就是說,整個run中的操作和主線程處於同一個線程。

    這樣對於那些簡單的操作,似乎並不會影響。但是對於耗時較長的操作,當它被加入到消息隊列中之後執行會佔用很長的時間,以至於處於同一線程的其他操作無法繼續執行,就會出現“假死”。爲了解決這個問題,就需要使得handler綁定到一個新開啓線程的消息隊列上,在這個處於另外線程的上的消息隊列中處理傳過來的Runnable對象和消息。SDK文檔中也提供了相關說明:

     When a process is created for your application, its main thread is dedicated to running a message queue that takes care of managing the top-level application objects (activities, broadcast receivers, etc) and any windows they create. You can create your own threads, and communicate back with the main application thread through a Handler. This is done by calling the same post or sendMessage methods as before, but from your new thread. The given Runnable or Message will than be scheduled in the Handler's message queue and processed when appropriate.

     具體操作方法如下:

 
 

  1. public class HandlerTest2 extends Activity {  
  2.  
  3.     @Override 
  4.     protected void onCreate(Bundle savedInstanceState) {  
  5.         // TODO Auto-generated method stub  
  6.     super.onCreate(savedInstanceState);  
  7.     setContentView(R.layout.main);  
  8.     //打印了當前線程的ID  
  9.     System.out.println("Activity-->" + Thread.currentThread().getId());  
  10.     //生成一個HandlerThread對象  
  11.     HandlerThread handlerThread = new HandlerThread("handler_thread");  
  12.     //在使用HandlerThread的getLooper()方法之前,必須先調用該類的start(),同時開啓一個新線程;  
  13.     handlerThread.start();
  14. //將由HandlerThread獲取的Looper傳遞給Handler對象,即由處於另外線程的Looper代替handler初始化時默認綁定的消息隊列來處理消息。  
  15.     MyHandler myHandler = new MyHandler(handlerThread.getLooper());  
  16.     Message msg = myHandler.obtainMessage();  
  17.     //將msg發送到目標對象,所謂的目標對象,就是生成該msg對象的handler對象  
  18.     Bundle b = new Bundle();  
  19.     b.putInt("age"20);  
  20.     b.putString("name""Jhon");  
  21.     msg.setData(b);  
  22.     msg.sendToTarget();  //將msg發送到myHandler
  23.     }  
  24.       
  25. //定義類
  26.     class MyHandler extends Handler{  
  27.     public MyHandler(){  
  28.               
  29.     }  
  30.     public MyHandler(Looper looper){  
  31.         super(looper);  
  32.     }  
  33.     @Override 
  34.     public void handleMessage(Message msg) {  
  35.         Bundle b = msg.getData();  
  36.         int age = b.getInt("age");  
  37.         String name = b.getString("name");  
  38.         System.out.println("age is " + age + ", name is" + name);  
  39.         System.out.println("Handler--->" + Thread.currentThread().getId());  
  40.         System.out.println("handlerMessage");  
  41.         }  
  42.     }  
  43. }  

    這樣,當使用sendMessage方法傳遞消息或者使用post方法傳遞Runnable對象時,就會把它們傳遞到與handler對象綁定的處於另外一個線程的消息隊列中,它們將在另外的消息隊列中被處理。而主線程還會在發送操作完成時候繼續進行,不會影響當前的操作。

    這裏需要注意,這裏用到的多線程並非由Runnable對象開啓的,而是ThreadHandler對象開啓的。Runnable對象只是作爲一個封裝了操作的對象被傳遞,並未產生新線程。

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