Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發實例 >> Android應用開發入門(四十二)多線程之Handler

Android應用開發入門(四十二)多線程之Handler

編輯:Android開發實例

前言

  Android的消息傳遞機制是另外一種形式的“事件處理”,這種機制主要是為了解決Android應用中多線程的問題,在Android中不允許Activity新啟動的線程訪問該Activity裡的UI組件,這樣會導致新啟動的線程無法改變UI組件的屬性值。但實際開發中,很多地方需要在工作線程中改變UI組件的屬性值,比如下載網絡圖片、動畫等等。本文主要介紹Handler是如何發送與處理線程上傳遞來的消息,並講解Message的幾種傳遞數據的方式,最後均會以小Demo來演示。

 

Handler

  Handler,它直接繼承自Object,一個Handler允許發送和處理Message或者Runnable對象,並且會關聯到主線程的MessageQueue中。每個Handler具有一個單獨的線程,並且關聯到一個消息隊列的線程,就是說一個Handler有一個固有的消息隊列。當實例化一個Handler的時候,它就承載在一個線程和消息隊列的線程,這個Handler可以把Message或Runnable壓入到消息隊列,並且從消息隊列中取出Message或Runnable,進而操作它們。

  Handler主要有兩個作用:

  • 在工作線程中發送消息。
  • 在UI線程中獲取、處理消息。

  上面介紹到Handler可以把一個Message對象或者Runnable對象壓入到消息隊列中,進而在UI線程中獲取Message或者執行Runnable對象,所以Handler把壓入消息隊列有兩大體系,Post和sendMessage:

  • Post:Post允許把一個Runnable對象入隊到消息隊列中。它的方法有:post(Runnable)、postAtTime(Runnable,long)、postDelayed(Runnable,long)。
  • sendMessage:sendMessage允許把一個包含消息數據的Message對象壓入到消息隊列中。它的方法有:sendEmptyMessage(int)、sendMessage(Message)、sendMessageAtTime(Message,long)、sendMessageDelayed(Message,long)。

  從上面的各種方法可以看出,不管是post還是sendMessage都具有多種方法,它們可以設定Runnable對象和Message對象被入隊到消息隊列中,是立即執行還是延遲執行。

  

Post

  對於Handler的Post方式來說,它會傳遞一個Runnable對象到消息隊列中,在這個Runnable對象中,重寫run()方法。一般在這個run()方法中寫入需要在UI線程上的操作。

  在Handler中,關於Post方式的方法有:

  • boolean post(Runnable r):把一個Runnable入隊到消息隊列中,UI線程從消息隊列中取出這個對象後,立即執行。
  • boolean postAtTime(Runnable r,long uptimeMillis):把一個Runnable入隊到消息隊列中,UI線程從消息隊列中取出這個對象後,在特定的時間執行。
  • boolean postDelayed(Runnable r,long delayMillis):把一個Runnable入隊到消息隊列中,UI線程從消息隊列中取出這個對象後,延遲delayMills秒執行
  • void removeCallbacks(Runnable r):從消息隊列中移除一個Runnable對象。

   下面通過一個Demo,講解如何通過Handler的post方式在新啟動的線程中修改UI組件的屬性:

  1. package com.bgxt.datatimepickerdemo;  
  2.  
  3. import android.app.Activity;  
  4. import android.os.Bundle;  
  5. import android.os.Handler;  
  6. import android.view.View;  
  7. import android.widget.Button;  
  8. import android.widget.TextView;  
  9.  
  10. public class HandlerPostActivity1 extends Activity {  
  11.     private Button btnMes1,btnMes2;  
  12.     private TextView tvMessage;  
  13.     // 聲明一個Handler對象  
  14.     private static Handler handler=new Handler();  
  15.       
  16.     @Override 
  17.     protected void onCreate(Bundle savedInstanceState) {  
  18.         super.onCreate(savedInstanceState);  
  19.         setContentView(R.layout.message_activity);          
  20.           
  21.         btnMes1=(Button)findViewById(R.id.btnMes1);  
  22.         btnMes2=(Button)findViewById(R.id.btnMes2);  
  23.         tvMessage=(TextView)findViewById(R.id.tvMessage);  
  24.         btnMes1.setOnClickListener(new View.OnClickListener() {  
  25.               
  26.             @Override 
  27.             public void onClick(View v) {  
  28.                 // 新啟動一個子線程  
  29.                 new Thread(new Runnable() {                      
  30.                     @Override 
  31.                     public void run() {  
  32.                         // tvMessage.setText("...");  
  33.                         // 以上操作會報錯,無法再子線程中訪問UI組件,UI組件的屬性必須在UI線程中訪問  
  34.                         // 使用post方式修改UI組件tvMessage的Text屬性  
  35.                         handler.post(new Runnable() {                      
  36.                             @Override 
  37.                             public void run() {  
  38.                                 tvMessage.setText("使用Handler.post在工作線程中發送一段執行到消息隊列中,在主線程中執行。");                          
  39.                             }  
  40.                         });                                  
  41.                     }  
  42.                 }).start();  
  43.             }  
  44.         });  
  45.           
  46.         btnMes2.setOnClickListener(new View.OnClickListener() {  
  47.               
  48.             @Override 
  49.             public void onClick(View v) {  
  50.                 new Thread(new Runnable() {                      
  51.                     @Override 
  52.                     public void run() {  
  53.                         // 使用postDelayed方式修改UI組件tvMessage的Text屬性值  
  54.                         // 並且延遲3S執行  
  55.                         handler.postDelayed(new Runnable() {  
  56.                               
  57.                             @Override 
  58.                             public void run() {  
  59.                                 tvMessage.setText("使用Handler.postDelayed在工作線程中發送一段執行到消息隊列中,在主線程中延遲3S執行。");      
  60.                                   
  61.                             }  
  62.                         }, 3000);                          
  63.                     }  
  64.                 }).start();  
  65.                   
  66.             }  
  67.         });  
  68.     }  

 

  效果展示:

  有一點值得注意的是,對於Post方式而言,它其中Runnable對象的run()方法的代碼,均執行在UI線程上,所以對於這段代碼而言,不能執行在UI線程上的操作,一樣無法使用post方式執行,比如說訪問網絡,下面提供一個例子,使用post方式從互聯網上獲取一張圖片,並且顯示在ImageView中。

 

  1. package com.bgxt.datatimepickerdemo;  
  2.  
  3. import org.apache.http.HttpResponse;  
  4. import org.apache.http.client.HttpClient;  
  5. import org.apache.http.client.methods.HttpGet;  
  6. import org.apache.http.impl.client.DefaultHttpClient;  
  7. import org.apache.http.util.EntityUtils;  
  8.  
  9. import android.app.Activity;  
  10. import android.app.ProgressDialog;  
  11. import android.graphics.Bitmap;  
  12. import android.graphics.BitmapFactory;  
  13. import android.os.Bundle;  
  14. import android.os.Handler;  
  15. import android.view.View;  
  16. import android.widget.Button;  
  17. import android.widget.ImageView;  
  18.  
  19. public class HandlerPostActivity2 extends Activity {  
  20.     private Button btnDown;  
  21.     private ImageView ivImage;  
  22.     private static String image_path = "http://ww4.sinaimg.cn/bmiddle/786013a5jw1e7akotp4bcj20c80i3aao.jpg";  
  23.     private ProgressDialog dialog;  
  24.     // 一個靜態的Handler,Handler建議聲明為靜態的  
  25.     private static  Handler handler=new Handler();  
  26.     @Override 
  27.     protected void onCreate(Bundle savedInstanceState) {  
  28.         super.onCreate(savedInstanceState);  
  29.         setContentView(R.layout.asynctask_activity);  
  30.           
  31.         btnDown = (Button) findViewById(R.id.btnDown);  
  32.         ivImage = (ImageView) findViewById(R.id.ivSinaImage);  
  33.  
  34.         dialog = new ProgressDialog(this);  
  35.         dialog.setTitle("提示");  
  36.         dialog.setMessage("正在下載,請稍後...");  
  37.         dialog.setCancelable(false);  
  38.           
  39.         btnDown.setOnClickListener(new View.OnClickListener() {              
  40.             @Override 
  41.             public void onClick(View v) {  
  42.                 // 開啟一個子線程,用於下載圖片  
  43.                 new Thread(new MyThread()).start();  
  44.                 // 顯示對話框  
  45.                 dialog.show();  
  46.             }  
  47.         });  
  48.     }  
  49.       
  50.     public class MyThread implements Runnable {  
  51.  
  52.         @Override 
  53.         public void run() {  
  54.             // 下載一個圖片  
  55.             HttpClient httpClient = new DefaultHttpClient();  
  56.             HttpGet httpGet = new HttpGet(image_path);  
  57.             HttpResponse httpResponse = null;  
  58.             try {  
  59.                 httpResponse = httpClient.execute(httpGet);  
  60.                 if (httpResponse.getStatusLine().getStatusCode() == 200) {  
  61.                     byte[] data = EntityUtils.toByteArray(httpResponse  
  62.                             .getEntity());  
  63.                     // 得到一個Bitmap對象,並且為了使其在post內部可以訪問,必須聲明為final  
  64.                     final Bitmap bmp=BitmapFactory.decodeByteArray(data, 0, data.length);  
  65.                     handler.post(new Runnable() {                          
  66.                         @Override 
  67.                         public void run() {  
  68.                             // 在Post中操作UI組件ImageView  
  69.                             ivImage.setImageBitmap(bmp);  
  70.                         }  
  71.                     });  
  72.                     // 隱藏對話框  
  73.                     dialog.dismiss();  
  74.                 }  
  75.             } catch (Exception e) {  
  76.                 e.printStackTrace();  
  77.             }  
  78.         }  
  79.  
  80.     }  

 

  效果展示:

 

Message

  Handler如果使用sendMessage的方式把消息入隊到消息隊列中,需要傳遞一個Message對象,而在Handler中,需要重寫handleMessage()方法,用於獲取工作線程傳遞過來的消息,此方法運行在UI線程上。下面先介紹一下Message。

  Message是一個final類,所以不可被繼承。Message封裝了線程中傳遞的消息,如果對於一般的數據,Message提供了getData()和setData()方法來獲取與設置數據,其中操作的數據是一個Bundle對象,這個Bundle對象提供一系列的getXxx()和setXxx()方法用於傳遞基本數據類型的鍵值對,對於基本數據類型,使用起來很簡單,這裡不再詳細講解。而對於復雜的數據類型,如一個對象的傳遞就要相對復雜一些。在Bundle中提供了兩個方法,專門用來傳遞對象的,但是這兩個方法也有相應的限制,需要實現特定的接口,當然,一些Android自帶的類,其實已經實現了這兩個接口中的某一個,可以直接使用。方法如下:

  • putParcelable(String key,Parcelable value):需要傳遞的對象類實現Parcelable接口。
  • pubSerializable(String key,Serializable value):需要傳遞的對象類實現Serializable接口。

  還有另外一種方式在Message中傳遞對象,那就是使用Message自帶的obj屬性傳值,它是一個Object類型,所以可以傳遞任意類型的對象,Message自帶的有如下幾個屬性:

  • int arg1:參數一,用於傳遞不復雜的數據,復雜數據使用setData()傳遞。
  • int arg2:參數二,用於傳遞不復雜的數據,復雜數據使用setData()傳遞。
  • Object obj:傳遞一個任意的對象。
  • int what:定義的消息碼,一般用於設定消息的標志。

   對於Message對象,一般並不推薦直接使用它的構造方法得到,而是建議通過使用Message.obtain()這個靜態的方法或者Handler.obtainMessage()獲取。Message.obtain()會從消息池中獲取一個Message對象,如果消息池中是空的,才會使用構造方法實例化一個新Message,這樣有利於消息資源的利用。並不需要擔心消息池中的消息過多,它是有上限的,上限為10個。Handler.obtainMessage()具有多個重載方法,如果查看源碼,會發現其實Handler.obtainMessage()在內部也是調用的Message.obtain()。  

  既然Message是在線程間傳遞消息,那麼先以一個Demo講解一下Message的使用,還是常規的從互聯網上下載一張圖片的Demo,下載後使用ImageView控件展示:

 

  1. package com.bgxt.datatimepickerdemo;  
  2.  
  3. import org.apache.http.HttpResponse;  
  4. import org.apache.http.client.HttpClient;  
  5. import org.apache.http.client.methods.HttpGet;  
  6. import org.apache.http.impl.client.DefaultHttpClient;  
  7. import org.apache.http.util.EntityUtils;  
  8.  
  9. import android.app.Activity;  
  10. import android.app.ProgressDialog;  
  11. import android.graphics.Bitmap;  
  12. import android.graphics.BitmapFactory;  
  13. import android.os.Bundle;  
  14. import android.os.Handler;  
  15. import android.os.Message;  
  16. import android.view.View;  
  17. import android.widget.Button;  
  18. import android.widget.ImageView;  
  19.  
  20. public class HandlerMessageActivity1 extends Activity {  
  21.     private Button btnDown;  
  22.     private ImageView ivImage;  
  23.     private static String image_path = "http://ww4.sinaimg.cn/bmiddle/786013a5jw1e7akotp4bcj20c80i3aao.jpg";  
  24.     private ProgressDialog dialog;  
  25.     private static int IS_FINISH = 1;  
  26.  
  27.     @Override 
  28.     protected void onCreate(Bundle savedInstanceState) {  
  29.         super.onCreate(savedInstanceState);  
  30.         setContentView(R.layout.asynctask_activity);  
  31.  
  32.         btnDown = (Button) findViewById(R.id.btnDown);  
  33.         ivImage = (ImageView) findViewById(R.id.ivSinaImage);  
  34.  
  35.         dialog = new ProgressDialog(this);  
  36.         dialog.setTitle("提示信息");  
  37.         dialog.setMessage("正在下載,請稍後...");  
  38.         dialog.setCancelable(false);  
  39.  
  40.         btnDown.setOnClickListener(new View.OnClickListener() {  
  41.             @Override 
  42.             public void onClick(View v) {  
  43.                     new Thread(new MyThread()).start();  
  44.                     dialog.show();  
  45.             }  
  46.         });  
  47.     }  
  48.  
  49.     private  Handler handler = new Handler() {  
  50.         // 在Handler中獲取消息,重寫handleMessage()方法  
  51.         @Override 
  52.         public void handleMessage(Message msg) {              
  53.             // 判斷消息碼是否為1  
  54.             if(msg.what==IS_FINISH){  
  55.                 byte[] data=(byte[])msg.obj;  
  56.                 Bitmap bmp=BitmapFactory.decodeByteArray(data, 0, data.length);  
  57.                 ivImage.setImageBitmap(bmp);  
  58.                 dialog.dismiss();  
  59.             }  
  60.         }  
  61.     };  
  62.  
  63.     public class MyThread implements Runnable {  
  64.  
  65.         @Override 
  66.         public void run() {  
  67.             HttpClient httpClient = new DefaultHttpClient();  
  68.             HttpGet httpGet = new HttpGet(image_path);  
  69.             HttpResponse httpResponse = null;  
  70.             try {  
  71.                 httpResponse = httpClient.execute(httpGet);  
  72.                 if (httpResponse.getStatusLine().getStatusCode() == 200) {  
  73.                     byte[] data = EntityUtils.toByteArray(httpResponse  
  74.                             .getEntity());  
  75.                     // 獲取一個Message對象,設置what為1  
  76.                     Message msg = Message.obtain();  
  77.                     msg.obj = data;  
  78.                     msg.what = IS_FINISH;  
  79.                     // 發送這個消息到消息隊列中  
  80.                     handler.sendMessage(msg);  
  81.                 }  
  82.             } catch (Exception e) {  
  83.                 e.printStackTrace();  
  84.             }  
  85.         }  
  86.     }  

 

  展示效果:

  Message.obtain()方法具有多個重載方法,大致可以分為為兩類,一類是無需傳遞Handler對象,對於這類的方法,當填充好消息後,需要調用Handler.sendMessage()方法來發送消息到消息隊列中。第二類需要傳遞一個Handler對象,這類方法可以直接使用Message.sendToTarget()方法發送消息到消息隊列中,這是因為在Message對象中有一個私有的Handler類型的屬性Target,當時obtain方法傳遞進一個Handler對象的時候,會給Target屬性賦值,當調用sendToTarget()方法的時候,實際在它內部還是調用的Target.sendMessage()方法。

  在Handler中,也定義了一些發送空消息的方法,如:sendEmptyMessage(int what)、sendEmptyMessageDelayed(int what,long delayMillis),看似這些方法沒有使用Message就可以發送一個消息,但是如果查看源碼就會發現,其實內部也是從Message.obtain()方法中獲取一個Message對象,然後給屬性賦值,最後使用sendMessage()發送消息到消息隊列中。

  Handler中,與Message發送消息相關的方法有:

  • Message obtainMessage():獲取一個Message對象。
  • boolean sendMessage():發送一個Message對象到消息隊列中,並在UI線程取到消息後,立即執行。
  • boolean sendMessageDelayed():發送一個Message對象到消息隊列中,在UI線程取到消息後,延遲執行。
  • boolean sendEmptyMessage(int what):發送一個空的Message對象到隊列中,並在UI線程取到消息後,立即執行。
  • boolean sendEmptyMessageDelayed(int what,long delayMillis):發送一個空Message對象到消息隊列中,在UI線程取到消息後,延遲執行。
  • void removeMessage():從消息隊列中移除一個未響應的消息。

  下面通過一個小Demo演示一下各種發送Message的方式:

 

  1. package com.bgxt.datatimepickerdemo;  
  2.  
  3. import android.app.Activity;  
  4. import android.os.Bundle;  
  5. import android.os.Handler;  
  6. import android.os.Message;  
  7. import android.view.View;  
  8. import android.widget.Button;  
  9. import android.widget.TextView;  
  10.  
  11. public class HandlerMessageActivity2 extends Activity {  
  12.     private Button btn1, btn2, btn3, btn4,btn5;  
  13.     private static TextView tvMes;  
  14.     private static Handler handler = new Handler() {  
  15.         @Override 
  16.         public void handleMessage(android.os.Message msg) {  
  17.             if (msg.what == 3||msg.what==5) {  
  18.                 tvMes.setText("what=" + msg.what + ",這是一個空消息");  
  19.             } else {  
  20.                 tvMes.setText("what=" + msg.what + "," + msg.obj.toString());  
  21.             }  
  22.  
  23.         };  
  24.     };  
  25.  
  26.     @Override 
  27.     protected void onCreate(Bundle savedInstanceState) {  
  28.         // TODO Auto-generated method stub  
  29.         super.onCreate(savedInstanceState);  
  30.         setContentView(R.layout.message_activity2);  
  31.         tvMes = (TextView) findViewById(R.id.tvMes);  
  32.         btn1 = (Button) findViewById(R.id.btnMessage1);  
  33.         btn2 = (Button) findViewById(R.id.btnMessage2);  
  34.         btn3 = (Button) findViewById(R.id.btnMessage3);  
  35.         btn4 = (Button) findViewById(R.id.btnMessage4);  
  36.         btn5 = (Button) findViewById(R.id.btnMessage5);  
  37.  
  38.         btn1.setOnClickListener(new View.OnClickListener() {  
  39.             @Override 
  40.             public void onClick(View v) {  
  41.                 // 使用Message.Obtain+Hander.sendMessage()發送消息  
  42.                 new Thread(new Runnable() {  
  43.                     @Override 
  44.                     public void run() {  
  45.                         Message msg = Message.obtain();  
  46.                         msg.what = 1;  
  47.                         msg.obj = "使用Message.Obtain+Hander.sendMessage()發送消息";  
  48.                         handler.sendMessage(msg);  
  49.                     }  
  50.                 }).start();  
  51.             }  
  52.         });  
  53.  
  54.         btn2.setOnClickListener(new View.OnClickListener() {  
  55.  
  56.             @Override 
  57.             public void onClick(View v) {  
  58.                 // 使用Message.sendToTarget發送消息  
  59.                 new Thread(new Runnable() {  
  60.                     @Override 
  61.                     public void run() {  
  62.                         Message msg = Message.obtain(handler);  
  63.                         msg.what = 2;  
  64.                         msg.obj = "使用Message.sendToTarget發送消息";  
  65.                         msg.sendToTarget();  
  66.                     }  
  67.                 }).start();  
  68.             }  
  69.         });  
  70.  
  71.         btn3.setOnClickListener(new View.OnClickListener() {  
  72.             // 發送一個延遲消息  
  73.             @Override 
  74.             public void onClick(View v) {  
  75.                 new Thread(new Runnable() {  
  76.                     @Override 
  77.                     public void run() {  
  78.                         handler.sendEmptyMessage(3);  
  79.                     }  
  80.                 }).start();  
  81.             }  
  82.         });  
  83.  
  84.         btn4.setOnClickListener(new View.OnClickListener() {  
  85.  
  86.             @Override 
  87.             public void onClick(View v) {  
  88.                 new Thread(new Runnable() {  
  89.                     @Override 
  90.                     public void run() {  
  91.                         Message msg = Message.obtain();  
  92.                         msg.what =4;  
  93.                         msg.obj = "使用Message.Obtain+Hander.sendMessage()發送延遲消息";  
  94.                         handler.sendMessageDelayed(msg, 3000);  
  95.                     }  
  96.                 }).start();  
  97.             }  
  98.         });  
  99.           
  100.         btn5.setOnClickListener(new View.OnClickListener() {  
  101.             // 發送一個延遲的空消息  
  102.             @Override 
  103.             public void onClick(View v) {  
  104.                 new Thread(new Runnable() {  
  105.                     @Override 
  106.                     public void run() {  
  107.                         handler.sendEmptyMessageDelayed(5, 3000);  
  108.                     }  
  109.                 }).start();  
  110.             }  
  111.         });  
  112.     }  

 效果展示:

  源碼下載

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