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

来源:互联网 发布:2016年交通意外数据 编辑:程序博客网 时间:2024/05/17 01:07

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;}@Overridepublic void run() {// TODO Auto-generated method stubsuper.run();Looper.prepare();//把Looper绑定到此线程mHandler=new Handler(){public void handleMessage(Message msg){switch(msg.what){//处理消息}}};//Handler绑定到此线程的LooperLooper.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 stubsuper.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
原创粉丝点击