一、概述
因為子線程的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對象的兩種方法)
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對象的方法
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對象的方法
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"/>
效果圖:
下載下傳前:
下載下傳後:
2、主線程向子線程發送消息
布局還是上面的那個,就不貼代碼了
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 }
主線程項子線程發送消息
效果圖: