android Handler 和Message消息机制

来源:互联网 发布:淘宝分类没法横向布置 编辑:程序博客网 时间:2024/09/21 06:17

最本质的多线程:hanlder和message机制:

       为何需要多线程:

在日常应用中,我们通常需要处理一些“后台,用户不可见”的操作,例如说,我们需要下载一个音乐,要是你的应用必须等用户下载完成之后才可以进行别的操作,那肯定让用户非常的不爽。这时候,我们通常的做法是,让这些操作去后台执行,然后等后台执行完毕之后,再给用户弹出相应的提示信息。这时候,我们就需要使用多线程机制,然后通过创建一个新的线程来执行这些操作。

明白了,实现需求,我们就准备着手实现了。但是,经过进一步的了解,我们悲剧的发现,android中的线程机制是,只能在UI线程中和用户进行交互。当我们创建了一个新线程,执行了一些后台操作,执行完成之后,我们想要给用户弹出对话框以确认,但是却悲剧的发现,我们根本无法返回UI主线程了。

(说明:何为UI线程:UI线程就是你当前看到的这些交互界面所属的线程)。

这时候,我们如果想要实现这些功能,我们就需要一个android为我们提供的handler和message机制。

先讲解下编程机制:

       我们通常在UI线程中创建一个handler,handler相当于一个处理器,它主要负责处理和绑定到该handler的线程中的message。每一个handler都必须关联一个looper,并且两者是一一对应的,注意,这点很重要哦!此外,looper负责从其内部的messageQueue中拿出一个个的message给handler进行处理。因为我们这里handler是在UI线程中实现的,所以经过这么一个handler、message机制,我们就可以回到UI线程中了。

       何为handler:处理后台进程返回数据的工作人员。

       何为message:后台进程返回的数据,里面可以存储bundle等数据格式

       何为messageQueue:是线程对应looper的一部分,负责存储从后台进程中抛回的和当前handler绑定的message,是一个队列。

       何为looper:looper相当于一个messageQueue的管理人员,它会不停的循环的遍历队列,然后将符合条件的message一个个的拿出来交给handler进行处理。

 

       注意,handler是在UI线程中声明的,如果我们直接用类似代码执行一个线程的话,实际上并没有创建一个新的线程,因为handler已经跟默认的UI线程中的looper绑定了。

 

Java代码  收藏代码
  1. private void test(){   
  2.         MyHandler handler = new MyHandler();   
  3.           handler.post(new Runnable(){   
  4.          @override   
  5.          public void run(){   
  6.            System.out.println("hello baby");   
  7.        }   
  8. } );   
  9. }  

  

 

 

 

如果有兴趣的话,可以去看下Handler的默认空构造函数便知道愿意了,里面直接绑定了当前UI线程的looper。

下面给出一个比较简单,并且实用的实例。

 

 

Java代码  收藏代码
  1. package cn.com.src;  
  2.   
  3. import cn.com.chenzheng_java.utils.R;  
  4. import android.app.Activity;  
  5. import android.os.Bundle;  
  6. import android.os.Handler;  
  7. import android.os.HandlerThread;  
  8. import android.os.Looper;  
  9. import android.os.Message;  
  10. import android.util.Log;  
  11. import android.view.View;  
  12. import android.view.View.OnClickListener;  
  13. import android.widget.Button;  
  14.   
  15. /** 
  16.  * @author chenzheng_java 
  17.  * handler和message测试用例 
  18.  */  
  19. public class HanlderMessageTest extends Activity implements OnClickListener{  
  20.     Button button ;  
  21.     MyHandler handler ;  
  22.   
  23.     @Override  
  24.     protected void onCreate(Bundle savedInstanceState) {  
  25.         super.onCreate(savedInstanceState);  
  26.         setContentView(R.layout.main);  
  27.         button = (Button) this.findViewById(R.id.button1);  
  28.         button.setOnClickListener(this);  
  29.           
  30.           
  31.           
  32.     }  
  33.       
  34.     // 声明自己的handler  
  35.     private class MyHandler extends Handler{  
  36.         /** 
  37.          * 使用默认的构造函数,会将handler绑定当前UI线程的looper。 
  38.          * 如果想使用多线程这里是不能使用默认的构造方法的。 
  39.          */  
  40.         public MyHandler() {  
  41.             super();  
  42.         }  
  43.           
  44.         public MyHandler(Looper looper){  
  45.             super(looper);  
  46.         }  
  47.           
  48.         // 处理具体的message,该方法由父类中进行继承.  
  49.         @Override  
  50.         public void handleMessage(Message msg) {  
  51.             int whatNumber = msg.what;  
  52.             Bundle bundle = (Bundle)msg.obj;  
  53.             Log.i("what", whatNumber+"");  
  54.             Log.i("名称", bundle.getString("name"));  
  55.             Log.i("性别", bundle.getString("sex"));  
  56.             Log.i("年龄", bundle.getString("age"));  
  57.             super.handleMessage(msg);  
  58.         }  
  59.           
  60.           
  61.     }  
  62.       
  63.     // 我自定义的任务,一般都会实现Runnable  
  64.     private class MyThread implements Runnable {  
  65.         /** 
  66.          * 该方法的内部进行具体的任务实现,比如 下载. 
  67.          * Message中包含着想和ui线程交互的数据,原则上,在线程内部是 
  68.          * 最好不要直接调用handler的。 
  69.          * */  
  70.         @Override  
  71.         public void run() {  
  72.               
  73.             try {  
  74.                 Thread.sleep(6000);  
  75.                 Message message = Message.obtain(handler);  
  76.                 message.what = 10 ;  
  77.                 Bundle bundle = new Bundle();  
  78.                 bundle.putString("name""chenzheng");  
  79.                 bundle.putString("sex""纯爷们");  
  80.                 bundle.putString("age""生卒年不详");  
  81.                 message.obj = bundle ;  
  82.                 Log.i("通知""开始发message了哦");  
  83.                 Log.i("通知thread_id:"""+Thread.currentThread().getId());  
  84.                 message.sendToTarget();  
  85.             } catch (Exception e) {  
  86.                 Log.i("通知""线程sleep时出错了!");  
  87.                 e.printStackTrace();  
  88.             }  
  89.           
  90.         }  
  91.           
  92.           
  93.       
  94.     }  
  95.   
  96.     @Override  
  97.     public void onClick(View v) {  
  98.         Log.i("通知thread_id:"""+Thread.currentThread().getId());  
  99.           
  100.         // 创建一个包含Looper的线程,这里如果没有HandlerThread的调用,会直接将后边的MyThread放到UI线程队列  
  101.         HandlerThread myHandlerThread = new HandlerThread("chenzheng_java");  
  102.         // 启动新线程  
  103.         myHandlerThread.start();  
  104.         // 将handler绑定到新线程  
  105.         handler = new MyHandler(myHandlerThread.getLooper());  
  106.         // 在新线程中执行任务   
  107.         handler.post(new MyThread());  
  108.           
  109.     }  
  110.       
  111.       
  112. }  

  

 

 

这里,很多人不理解,HandlerThread到底是干什么的?

       HandlerThread实际上继承自Thread,属于Thread的加强版。在其内部添加了和该Thread相绑定的loop,

Java代码  收藏代码
  1. public void run() {  
  2.     mTid = Process.myTid();  
  3.       
  4.     Looper.prepare();  
  5.     synchronized (this) {  
  6.         mLooper = Looper.myLooper()  
  7.         notify();  
  8.     }  
  9.     Process.setThreadPriority(mPriority);  
  10.     onLooperPrepared();  
  11.     Looper.loop();  
  12.     mTid = -1;  
  13. }  

 

 

 

从代码中可以看出主要的区别就在于调用了Looper.prepare()、Looper.loop()以启用和开始消息队列。我们自己也可以继承Thread并模仿HandlerThread做一个线程类用于启动新的线程。

 

Message.sendToTarger()请不要用handler.sendMessage()等方法跑出,因为这样message会将target直接绑定到调用的handler上,还是那句老话,不建议在任务内部出现对handler的直接调用。当然,如果你一定要那么做,我只能说,OK,没问题的。

读到这里,如果你还不明白handler和looper是一一对应的,那你只能倒回去再看几遍了。

源码的一些理解:

       Handler:handler最主要的用途还是对消息进行分发和处理,所以我们在提供handler的时候,是被严格要求继承重写handlerMessage方法的。它是很被动的。

       Message:可以简单的认为是一个Object对象。只不过有些特殊而已,因为它有一个what属性来与其他message进行区分。可以通过Message message = Message.obtain(handler);或者Message message = Message.obtain();message.setTarget这两种方式指定messagehandler。记住,此处一定要显示的指定handler,否则程序会报出空指针异常哦。

 

       MessageQueue:其内部是通过一个ArrayList来实现的,并不是简单的先进先出哦,而是有优先级的。如果我们不对线程优先级进行设计,默认的是先进先出,但是如果我们显示的设置了线程的优先级,是会起作用的哦。

       Looper:上面我们说handler是被动的,为什么呢,原因就是looper的存在。Looper的实现就类似一个while(true)的循环一样,然后从里面读取数据,如果读到存在message,就将其传递给相应的handler进行处理。

其他,在android还提供了一些其他的帮助类:

AsyncTask:其内部实现机制就是handler和message机制。通过它可以方便的实现启动一个线程去后台执行复杂操作的需求。

AsyncQueryHandler实际上继承自Handler,它并不和handler一样属于android.os包中,它属于android.content包中,仅仅凭此,我们就知道,这是为了给content内部的应用进行定制的类。实际上,也确实如此,该类是专门为内容提供者提供异步执行的类,该类在哪一个线程中new的,该任务就会在当前线程中执行哦。如图

我们可以看到,除了必要的增删改查之外,还提供了一些在增删改查完成之后执行的相应方法,在这里实际上我们可以添加一些更新展示给用户的数据等操作。

一定要注意,该类直接就绑定了当前线程的looper,它也没有提供有Looper参数的构造方法,所以这点一定要注意。

0 0
原创粉丝点击