码迷,mamicode.com
首页 > 其他好文 > 详细

Handler,Looper,Message,MessageQueue,HandlerThread使用总结(上)

时间:2016-05-13 15:05:55      阅读:243      评论:0      收藏:0      [点我收藏+]

标签:

在安卓程序中,经常会有一些耗时的操作例如下载,网络访问等,如果将这些放在主线程执行,会很耗时,这样可能会导致一个异常 叫ANR异常(Application Not Responding)将会阻塞UI线程,从而会导致程序无响应。因此我们会将一些耗时操作放在子线程进行,但是由于android的UI操作并不是线程安全的,因此如果多个线程同时操作UI的话,会导致线程安全问题,因此android制订了一条规则,只允许UI线程(即主线程)进行UI操作,那么我们如何知道子线程何时操作完成呢?例如子线程下载好图片以后通知主线程进行视图更新。

Message简介

子线程任务完成以后需要发送消息给主线程,这个消息就是Message的一个实例,定义Message时需要定义三个实例变量

1、what   int型消息代码,用来描述消息

2、obj      随消息发送的用户指定对象

3、target  处理该消息的Handler


创建Message的方法

1、new一个就行

2、使用Handler.obtainMessage(...)该方法可以使我们从公共循环池中获取到一个Message实例,效率会提高

技术分享

技术分享技术分享

因此我们使用该方法会好些


MessageQueue简介

MessageQueue(消息队列)用来存放Message,采用先进先出的方式发出Message,


Looper简介

Looper叫做消息循环,他会不断的检查 MessageQueue上是否有新消息,然后抓取消息,完成指定的任务。每个线程有且只有一个Looper对象,用来管理MessageQueue。主线程也有Looper,它会自动创建,主线程的所有工作都是由他的Looper完成的。Looper主要有两个方法

1、Looper.prepare     用以启用Looper

2、Looper.loop           让Looper开始工作,从消息队列中抓取处理消息



Handler简介

要处理消息以及消息指定的任务时就需要用到Handler,她可以发出新消息到MessageQueue上,也可以读取Looper从MessageQueue上获取的消息。一个Handler仅与一个Looper相关联,一个Message也仅与一个目标Hander相关联。

Handler发送消息的方法:

技术分享技术分享

Looper获取到消息后,会交由消息的目标(即消息的target属性)处理,消息一般是在Handler.handleMessage(...)方法中处理

技术分享技术分享


关系图示

技术分享

技术分享


在简单了解过这些知识后,我们写一个小程序,利用了Handler与Message。该应用的主界面仅有一个ImageView故代码不再给出,主要功能是开启一个子线程模拟下载图片,然后下载完成后在UI线程上进行更新。


主要代码

声明handler,模拟下载到的图片的资源Id数组

技术分享

<span style="font-size:18px;"><pre name="code" class="java"><span style="font-size:18px;"><span style="white-space:pre">	</span>private ImageView mImageView;
	// 定义一个Handler
	private Handler mHandler;

	private int mIndex;
	// 存放照片资源id的数组
	int[] imageIds = new int[] { R.drawable.pic1, R.drawable.pic2,
			R.drawable.pic3, R.drawable.pic4

	};</span></span>




模拟下载图片的方法

技术分享

<span style="font-size:18px;">// 模拟下载的操作,随机生成一个数字
	public int virtualDown() {
		Random ran = new Random();
		int value = ran.nextInt(5);
		return value;
	}</span>


技术分享

使用TimerTask开启一个下载的子线程,获取到消息,并发送

<span style="font-size:18px;"><span style="font-size:18px;">// 开启一个TimerTask模拟下载的子线程
		new Timer().schedule(new TimerTask() {

			@Override
			public void run() {

				Bundle bundle = new Bundle();
				bundle.putInt("value", virtualDown());
				// 获取到消息
				// Message msg = new Message();
				Message msg = mHandler.obtainMessage();
				msg.what = 0x1233;
				msg.setData(bundle);
				// 发送消息,并将生成的数字传递过去
				mHandler.sendMessage(msg);
				

			}
		}, 0, 2000);
	}</span></span>


生成handle实例并在handleMessage中处理消息

技术分享技术分享

<span style="font-size:18px;"><span style="white-space:pre">	</span>mHandler = new Handler() {

			@Override
			// 处理消息的方法
			public void handleMessage(Message msg) {
				// what属性是消息的标识,用来区分每个消息
				if (msg.what == 0x1233) {
					mIndex = msg.getData().getInt("value");
					// 设置图片的显示,这是在主线程进行的
					switch (mIndex) {
					case 0:
						mImageView.setImageResource(imageIds[0]);
						break;
					case 1:
						mImageView.setImageResource(imageIds[1]);
						break;
					case 2:
						mImageView.setImageResource(imageIds[2]);
						break;
					case 3:
						mImageView.setImageResource(imageIds[3]);
						break;
					}
				}
			}
		};</span>



运行程序我们就可以看到主界面的图片每隔两秒就会变化一次了,当然图片是事先准备好的,并非下载的,过段时间博主会实现真正的下载。


在上面的程序中为什么没有见到Looper呢?这是因为主线程在启动过程中自动创建了Looper。而子线程默认是不带Looper的,所以我们就需要自己创建Looper,这时候就需要用到上述的两个方法,Looper.prepare和Looper.loop启动Looper了,官方的API Demo的示例为:

技术分享

class LooperThread extends Thread {
		public Handler mHandler;

		public void run() {
			Looper.prepare();
			mHandler = new Handler() {
				public void handleMessage(Message msg) {
					// process incoming messages here
				}
			};
			Looper.loop();
		}
	}






Handler,Looper,Message,MessageQueue,HandlerThread使用总结(上)

标签:

原文地址:http://blog.csdn.net/freshxu/article/details/51385593

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!