天天看點

安卓開發_深入了解Handler消息傳遞機制

一、概述

因為子線程的run()方法無法修改UI線程(主線程)的UI界面,是以Android引入了Handler消息傳遞機制,實作在新建立的線程中操作UI界面

二、消息類(Message)

消息類是存放在MessageQueue中的,而一個MessageQueue中可以包含多個Message對象

每一個Message對象可以通過Message.obtain()或者Handler.obtainMessage()方法獲得

一個Message具有的屬性:

屬性 類型 介紹
arg1 int 存放整型資料
arg2
obj Object 存放Object類型的任意對象
replyTo Message 指定此Message發送到哪裡的可選Message對象
what           指定使用者自定義的消息代碼,接受者可以了解這個消息的資訊

一個Message對象可以攜帶int類型的資料,而如果要攜帶其他類型的資料,可以将要攜帶的資料儲存到Bundle對象中,然後通過Message類的setDate()方法将其添加到Message中

注:

1、盡量使用Message.what辨別資訊,友善用于不同的方式處理Message

2、通常情況下,盡量使用Message.obtain()或者Handler.obtainMessage()方法從消息池中獲得空消息對象,以便節省資源,而不是使用Message的預設構造方法

3、當一個Message對象隻需要攜帶int型資料的時候,優先使用Message.arg1或Message.arg2屬性,這要比用Bundle攜帶int資料節省記憶體

三、消息處理類(Handler)

Handler 允許 發送或者處理 Message或者Runnable 類的對象到其(Handler)所線上程的MessageQueue中

主要有兩個作用:

1、連接配接主線程和子線程進行通信(UI線程和工作線程通信)

2、将Message對象 通過post()或者sendMessage()方法發送到MessageQueue中,

當MessageQueue循環到該對象時,調用相應的Handler對象的handlerMessage()方法進行處理

Handler類提供的部分方法:

方法
handleMessage(Message msg)

處理消息的方法。

通常使用該方法處理消息,

在發送消息時,該方法會自動回調

Post(Runnable r)

立即發送Runnable對象,

注:該Runnable對象最終将被封裝成Mwssage對象

PostAtTime(Runnable r,long uptimeMillis) 定時發送Runnable對象,
postDelayed(Runnable r,long delayMillis) 延遲發送Runnable對象,
sendEmptyMessage(int what) 發送空消息
sendMessage(Message msg) 立即發送消息
sendMessageAtTime(Message msg) 定時發送消息
sendMessageDelayed(Message msg,long delayMillis) 延遲發送消息

注:在一個線程中,隻能有一個Looper和MessageQueue,卻可以有多個Handler,這些Handler共享同一個Looper和MessageQueue

四、循環着(Looper)

1、從上面隻是可以知道:一個線程中,隻能有一個Looper和MessageQueue。

也就是說一個線程對應一個Looper對象,而一個Looper對象又對應一個MessageQueue(消息隊列),一個MessageQueue又存放着多條Message

注意:MessageQueue中,消息的存放時FIFO(先進先出)的。

2、Looper對象是為了一個線程開啟一個消息循環,來操作MessageQueue

注:在主線程中,系統會為主線程自動建立一個Looper對象來開啟消息循環。而在非主線程中,是沒有Looper對象的,沒有Looper對象就不能建立Handler對象

so,在主線程中直接建立Handler對象是可以的。但是在非主線程中建立Handler對象則會産生異常

3、如果需要在非主線建立Handler對象

(1)使用Looper類的prepare()方法來初始化一個Looper對象

(2)建立Handler對象

(3)使用Looper類的loop()方法啟動Looper,開啟消息循環

//需要先有Looper對象
            Looper.prepare();//會建立一個Looper對象,并把該對象放入到該線程的本地變量中,在Looper的構造方法中建立了MessageQueue對象
            //在子線程中執行個體化handler,子線程中沒有Looper對象 
            
            handler = new Handler(){
                
            };//如果直接執行個體化Handler,會異常 new RuntimeException,原因是子線程中沒有Looper對象 
            
            //讓Looper對象循環讀取MessageQueue中的消息
            //循環從隊列中讀取消息,當讀到消息時,會去調用   msg.target.dispatchMessage(msg);
            //在Message類中有一個target成員,當發送消息時,target成員被指派為目前的 handler對象
            Looper.loop();      

Looper提供的部分方法:

描述
prepare() 用于初始化Looper
loop() 啟動Looper線程,線程循環消息隊列(MessageQueue)擷取和處理資訊
myLooper() 獲得目前線程的Looper對象
getThread 獲得Looper對象所屬的線程
quit() 結束Looper循環

*注*:Looper.loop()方法,這個方法是循環消息隊列!即這個方法内部相當于正在執行一個死循環,是以在Looper.loop()代碼之後的代碼都不會執行

,而隻有再調用 Looper.quit()之後才會執行後面的代碼

-------------------------------------------------------------華麗的分割線------------------------------------------------------------------

讓我們看幾個例子來深入了解下Handler消息傳遞機制

1、子線程向主線程發送消息

在主線程中啟動一個子線程下載下傳圖檔,子線程傳消息遞給主線程,讓主線程處理。(了解子線程發送Runnable對象和發送Message對象的兩種方法)

安卓開發_深入了解Handler消息傳遞機制
安卓開發_深入了解Handler消息傳遞機制
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     xmlns:tools="http://schemas.android.com/tools"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5     android:orientation="vertical"
 6     android:gravity="center_horizontal"
 7      >
 8 
 9     <ImageView 
10         android:layout_width="200dp"
11         android:layout_height="200dp"
12         android:id="@+id/show_image"
13         android:src="@drawable/ic_launcher"
14         />
15     
16     <Button 
17         android:layout_width="wrap_content"
18         android:layout_height="wrap_content"
19         android:id="@+id/down_image"
20         android:text="下載下傳"
21         />
22 
23 </LinearLayout>      

布局檔案代碼

(1)發送Message對象的方法

安卓開發_深入了解Handler消息傳遞機制
安卓開發_深入了解Handler消息傳遞機制
1 package com.xqx.handle;
 2 
 3 import java.io.IOException;
 4 
 5 import org.apache.http.HttpResponse;
 6 import org.apache.http.client.ClientProtocolException;
 7 import org.apache.http.client.HttpClient;
 8 import org.apache.http.client.methods.HttpGet;
 9 import org.apache.http.impl.client.DefaultHttpClient;
10 import org.apache.http.util.EntityUtils;
11 
12 import android.app.Activity;
13 import android.graphics.Bitmap;
14 import android.graphics.BitmapFactory;
15 import android.os.Bundle;
16 import android.os.Handler;
17 import android.os.Message;
18 import android.view.View;
19 import android.view.View.OnClickListener;
20 import android.widget.Button;
21 import android.widget.ImageView;
22 
23 
24 public class MainActivity extends Activity {
25     private String path = "http://images2015.cnblogs.com/blog/493196/201509/493196-20150911220222090-1272536050.jpg";//圖檔下載下傳位址
26     private ImageView showImage;
27     private Button downImage;
28     //哪一個線程是接受消息的,就在哪個線程中執行個體化Handler對象
29     //因為主線程中,系統會為自動建立Looper對象,開啟循環消息,是以隻需要在主線程中定義Handler對象
30     private Handler handler = new Handler(){
31         @Override //處理消息的方法,當消息發送過來時,該方法自動回調
32         public void handleMessage(android.os.Message msg) {
33             //處理方法,将圖檔顯示在ImageView中
34             showImage.setImageBitmap((Bitmap) msg.obj);
35             
36         };
37     };
38     @Override
39     protected void onCreate(Bundle savedInstanceState) {
40         // TODO Auto-generated method stub
41         super.onCreate(savedInstanceState);
42         setContentView(R.layout.activity_main);
43         
44         showImage = (ImageView) findViewById(R.id.show_image);
45         downImage = (Button) findViewById(R.id.down_image);
46         
47         downImage.setOnClickListener(new OnClickListener() {
48             
49             @Override
50             public void onClick(View v) {
51                 // TODO Auto-generated method stub
52                 //開啟一個線程下載下傳圖檔
53                 new Thread(new Runnable() {
54                     
55                     @Override
56                     public void run() {
57                         // TODO Auto-generated method stub
58                         HttpGet get = new HttpGet(path);
59                         HttpClient client = new DefaultHttpClient();
60                         HttpResponse response = null;
61                         
62                         try {
63                             response = client.execute(get);
64                             if(response.getStatusLine().getStatusCode()==200)
65                             {
66                                 //獲得下載下傳後的圖檔的位元組資料
67                                 byte b[] = EntityUtils.toByteArray(response.getEntity());
68                                 //将圖檔位元組資料轉換成Bitmap格式
69                                  Bitmap bitmap = BitmapFactory.decodeByteArray(b, 0, b.length);
70                                  //下載下傳完成,将圖檔發送給主線程
71                                  //從MessageQueue中擷取可用的Message對象,如果沒有可用的,則會建立一個新的Message對象
72                                  Message msg = Message.obtain();
73                                  //把發送的圖檔放到msg對象中
74                                  msg.obj = bitmap;
75                                  //使用Handler.sendMessage(Message msg)方法傳遞消息
76                                  handler.sendMessage(msg);
77                             }
78                             
79                             
80                         } catch (ClientProtocolException e) {
81                             // TODO Auto-generated catch block
82                             e.printStackTrace();
83                         } catch (IOException e) {
84                             // TODO Auto-generated catch block
85                             e.printStackTrace();
86                         }
87                         
88                         
89                     }
90                 }).start();//不要忘記開啟線程
91             }
92         });
93         
94     }
95     
96     
97 }      

Handler.sendMessage(Message msg)

(2)、發送Runnable對象的方法

安卓開發_深入了解Handler消息傳遞機制
安卓開發_深入了解Handler消息傳遞機制
1 package com.xqx.handle;
 2 
 3 import java.io.IOException;
 4 
 5 import org.apache.http.HttpResponse;
 6 import org.apache.http.client.ClientProtocolException;
 7 import org.apache.http.client.HttpClient;
 8 import org.apache.http.client.methods.HttpGet;
 9 import org.apache.http.impl.client.DefaultHttpClient;
10 import org.apache.http.util.EntityUtils;
11 
12 import android.app.Activity;
13 import android.graphics.Bitmap;
14 import android.graphics.BitmapFactory;
15 import android.os.Bundle;
16 import android.os.Handler;
17 import android.os.Message;
18 import android.view.View;
19 import android.view.View.OnClickListener;
20 import android.widget.Button;
21 import android.widget.ImageView;
22 
23 
24 public class Handler2 extends Activity {
25     private String path = "http://images2015.cnblogs.com/blog/493196/201509/493196-20150911220222090-1272536050.jpg";//圖檔下載下傳位址
26     private ImageView showImage;
27     private Button downImage;
28     //哪一個線程是接受消息的,就在哪個線程中執行個體化Handler對象
29     //因為主線程中,系統會為自動建立Looper對象,開啟循環消息,是以隻需要在主線程中定義Handler對象
30     private Handler handler = new Handler();
31     @Override
32     protected void onCreate(Bundle savedInstanceState) {
33         // TODO Auto-generated method stub
34         super.onCreate(savedInstanceState);
35         setContentView(R.layout.activity_main);
36         
37         showImage = (ImageView) findViewById(R.id.show_image);
38         downImage = (Button) findViewById(R.id.down_image);
39         
40         downImage.setOnClickListener(new OnClickListener() {
41             
42             @Override
43             public void onClick(View v) {
44                 // TODO Auto-generated method stub
45                 //開啟一個線程下載下傳圖檔
46                 new Thread(new Runnable() {
47                     
48                     private Bitmap bitmap;
49 
50                     @Override
51                     public void run() {
52                         // TODO Auto-generated method stub
53                         HttpGet get = new HttpGet(path);
54                         HttpClient client = new DefaultHttpClient();
55                         HttpResponse response = null;
56                         
57                         try {
58                             response = client.execute(get);
59                             if(response.getStatusLine().getStatusCode()==200)
60                             {
61                                 //獲得下載下傳後的圖檔的位元組資料
62                                 byte b[] = EntityUtils.toByteArray(response.getEntity());
63                                 bitmap = BitmapFactory.decodeByteArray(b, 0, b.length);
64                                  //下載下傳完成,将圖檔發送給主線程
65                                 //Handler.post(Runnable r)方法
66                                  handler.post(new Runnable() {
67                                     
68                                     @Override
69                                     public void run() {
70                                         // TODO Auto-generated method stub
71                                         showImage.setImageBitmap(bitmap);
72                                     }
73                                 });
74                             }
75                             
76                             
77                         } catch (ClientProtocolException e) {
78                             // TODO Auto-generated catch block
79                             e.printStackTrace();
80                         } catch (IOException e) {
81                             // TODO Auto-generated catch block
82                             e.printStackTrace();
83                         }
84                         
85                         
86                     }
87                 }).start();//不要忘記開啟線程
88             }
89         });
90         
91     }
92     
93     
94 }      

Handler.post(Runnable r)

最後因為下載下傳網絡圖檔,不要忘記在清單檔案中 添加網絡通路權限

<uses-permission android:name="android.permission.INTERNET"/>      

效果圖:

下載下傳前:

安卓開發_深入了解Handler消息傳遞機制

下載下傳後:

安卓開發_深入了解Handler消息傳遞機制

2、主線程向子線程發送消息

布局還是上面的那個,就不貼代碼了

安卓開發_深入了解Handler消息傳遞機制
安卓開發_深入了解Handler消息傳遞機制
1 package com.xqx.handle;
 2 
 3 import android.app.Activity;
 4 import android.graphics.Color;
 5 import android.os.Bundle;
 6 import android.os.Handler;
 7 import android.os.Looper;
 8 import android.os.Message;
 9 import android.view.View;
10 import android.view.View.OnClickListener;
11 import android.widget.Button;
12 import android.widget.ImageView;
13 import android.widget.Toast;
14 
15 public class Handler3 extends Activity{
16     
17     private Button changeImage;
18     private ImageView showImage;
19     private Handler handler;
20     
21     @Override
22     protected void onCreate(Bundle savedInstanceState) {
23         // TODO Auto-generated method stub
24         super.onCreate(savedInstanceState);
25         setContentView(R.layout.activity_main);
26         
27         changeImage = (Button) findViewById(R.id.down_image);
28         showImage = (ImageView) findViewById(R.id.show_image);
29         MyThread mt = new MyThread();
30         Thread h = new Thread(mt);
31         h.start();
32         changeImage.setOnClickListener(new OnClickListener() {
33             
34             @Override
35             public void onClick(View v) {
36                 // TODO Auto-generated method stub
37                 //主線程發送消息
38                 Message msg = Message.obtain();
39                 msg.what = 1;
40                 handler.sendEmptyMessage(1);
41             }
42         });
43         
44     }
45     
46     class MyThread implements Runnable
47     {
48 
49         @Override
50         public void run() {
51             // TODO Auto-generated method stub
52             Looper.prepare();
53             handler = new Handler(){
54                 @Override
55                 public void handleMessage(Message msg) {
56                     // TODO Auto-generated method stub
57                     super.handleMessage(msg);
58                     int number = msg.what;
59                     Toast.makeText(getApplicationContext(), "子線程接收到了主線程的消息,消息内容為:"+number, 1).show();
60                 }
61             };
62             Looper.loop();
63         }
64         
65     }
66 
67 }      

主線程項子線程發送消息

效果圖:

安卓開發_深入了解Handler消息傳遞機制

繼續閱讀