一、概述
因为子线程的run()方法无法修改UI线程(主线程)的UI界面,所以Android引入了Handler消息传递机制,实现在新创建的线程中操作UI界面
二、消息类(Message)
消息类是存放在MessageQueue中的,而一个MessageQueue中可以包含多个Message对象
每一个Message对象可以通过Message.obtain()或者Handler.obtainMessage()方法获得
一个Message具有的属性:
属性 | 类型 | 介绍 |
---|---|---|
arg1 | int | 存放整型数据 |
arg2 | int | 存放整型数据 |
obj | Object | 存放Object类型的任意对象 |
replyTo | Message | 指定此Message发送到哪里的可选Message对象 |
what | int | 指定用户自定义的消息代码,接受者可以了解这个消息的信息 |
一个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对象, 注:该Runnable对象最终将被封装成Mwssage对象 |
postDelayed(Runnable r,long delayMillis) | 延迟发送Runnable对象, 注:该Runnable对象最终将被封装成Mwssage对象 |
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 }
(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 }
最后因为下载网络图片,不要忘记在清单文件中 添加网络访问权限
<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 }
效果图: