多线程和同步之Handler和Looper(三)

Android在android.os包中定义了两个类,它们通常是多线程应用线程间通信的基石:Handler和Looper

AsyncTask对象隐藏了Handler和Looper的细节,在某些情况还是要直接跟Handler和Looper打交道,比如把Runnnable对象传递到主线程之外的线程


Message:消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理。

Handler:处理者,负责Message的发送及处理。使用Handler时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等。

MessageQueue:消息队列,用来存放Handler发送过来的消息,并按照FIFO规则执行。当然,存放Message并非实际意义的保存,而是将Message以链表的方式串联起来的,等待Looper的抽取。

Looper:消息泵,不断地从MessageQueue中抽取Message执行。因此,一个MessageQueue需要一个Looper

Thread:线程,负责调度整个消息循环,即消息循环的执行场所。


Handler的使用:

public class MyThread extends Thread{
		private static final String TAG="MyThread";
		private Handler mHandler;
		public MyThread(String name) {
			super(name);

		}
		public Handler getmHandler() {
			return mHandler;
		}
		@Override
		public void run() {
			// TODO Auto-generated method stub
			super.run();
			Looper.prepare();//把Looper绑定到此线程
			mHandler=new Handler(){
				public void handleMessage(Message msg){
					switch(msg.what){
					//处理消息
					}
				}
			};
			//Handler绑定到此线程的Looper
			Looper.loop();//调用loop()去启动消息循环
		}		
	}
注:Handler对象在run()方法中创建,因为它需要绑定到指定的Looper,这个Looper就是在run()方法中调用Looper.prepare()创建的,因此在线程产生之前,调用getHandler() 将返回null


Looper的使用:

public class MyHandlerThread extends HandlerThread{
		private static final String TAG="MyHandlerThread";
		private Handler mHandler;
		public MyHandlerThread(String name) {
			super(name);

		}
		public Handler getmHandler() {
			return mHandler;
		}
		public void start() {
			// TODO Auto-generated method stub
			super.start();
			Looper looper=getLooper();//这里会一直阻塞到线程的Looper对象初始化结束
			mHandler=new Handler(looper){
				public void handleMessage(Message msg){
					switch(msg.what){
					//处理消息
					}
				}
			};
		}		
	}

android中的线程

1. 不要阻塞UI线程. 如果在UI线程中执行阻塞或者耗时操作会导致UI线程无法响应用户请求.

2. 不能在非UI线程(也称为工作线程)中更新UI, 这是因为android的UI控件都是线程不安全的.

由上所述, 开发者经常会启动工作线程完成耗时操作或阻塞操作, 如果需要在工作线程的执行期间更新UI状态, 则应该通知UI线程来进行.

线程间通信

请看下面的代码:

Java代码   收藏代码
  1. public void onClick(View v) {  
  2.     new Thread(new Runnable() {  
  3.         public void run() {  
  4.             Bitmap b = loadImageFromNetwork("http://example.com/image.png");  
  5.             mImageView.setImageBitmap(b);  
  6.         }  
  7.     }).start();  
  8. }  

上面的代码是错误的, mImageView.setImageBitmap(b)违反了第二条准则--不能在工作线程中更新UI. 

线程间通信可以解决工作线程如何通知UI线程更新控件的问题. android提供了3种线程间通信的方案:

1. 调用以下方法:

Activity.runOnUiThread(Runnable)

View.post(Runnable)

View.postDelayed(Runnable, long)

如果在工作线程中调用了这3个方法, 那么方法中Runnable参数封装的操作会在UI线程中执行.

使用这种方式可以修正例子中的错误之处:

Java代码   收藏代码
  1. public void onClick(View v) {  
  2.     new Thread(new Runnable() {  
  3.         public void run() {  
  4.             final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png");  
  5.             mImageView.post(new Runnable() {  
  6.                 // run方法会在UI线程中执行  
  7.                 public void run() {  
  8.                     mImageView.setImageBitmap(bitmap);  
  9.                 }  
  10.             });  
  11.         }  
  12.     }).start();  
  13. }  

2. Handler机制. Handler机制允许开发者在工作线程中调用与UI线程绑定的handler对象的sendMessage()方法向UI线程的消息队列发送一条消息, UI线程会在适当的时候从消息队列中取出消息并完成处理.

3. 使用AsyncTask类. 创建一个AsyncTask类的子类, 并根据需要选择覆写onPreExecute(), doInBackground(), onProgressUpdate(), onPostExecute()方法.AsyncTask类的具体使用方法请参看文档, 以下是一些大概的说明:

a. AsyncTask类是一个泛型类, 存在3个泛型参数. 第一个参数指定execute方法的参数类型, 第二个参数指定onProgressUpdate()方法的参数类型, 第三个参数指定 doInBackground()方法的返回值类型以及onPostExecute()方法的参数类型.

b. 执行流程: 在UI线程中调用AsyncTask类的execute方法(只有该步骤是由程序员控制的)-->系统调用onPreExecute(), 这个方法在UI线程中执行-->系统调用doInBackground()方法, 这个方法在工作线程中执行-->在doInBackground()方法中每调用一次publishProgress()方法, 就会在UI线程中执行一次onProgressUpdate()方法-->doInBackground()方法执行完成后, 系统将调用 onPostExecute()方法, 并将doInBackground()方法的返回值传递给 onPostExecute()方法的形参.  onPostExecute()方法在UI线程中执行.

采用这种方式也可以修正例子中的错误之处:

Java代码   收藏代码
  1. public void onClick(View v) {  
  2.     new DownloadImageTask().execute("http://example.com/image.png");  
  3. }  
  4. private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {  
  5.     protected Bitmap doInBackground(String... urls) {  
  6.         return loadImageFromNetwork(urls[0]);  
  7.     }  
  8.       
  9.     protected void onPostExecute(Bitmap result) {  
  10.         mImageView.setImageBitmap(result);  
  11.     }  
  12. }  


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值