Android 消息机制(Handler,Looper,MessageQueue,Message)

来源:互联网 发布:外国女朋友 知乎 编辑:程序博客网 时间:2024/05/14 16:06

android的消息处理有三个核心类:Looper,Handler和Message。其实还有一个Message Queue(消息队列),但是MQ被封装到Looper里面了,Handler扮演了往MQ上添加消息和处理消息的角色(只处理由自己发出的消息),即通知MQ它要执行一个任务(sendMessage),并在loop到自己的时候执行该任务(handleMessage),整个过程是异步的。

   消息处理过程如下图所示:

 

Handler主要涉及到的内容有:

  Handler 、 Handler.CallbackAsyncQueryHandler

  Looper

  HandlerThreadRunnable

  MessageMessage queue

Looper

Android中的Looper类,是用来封装消息循环和消息队列的一个类,用于在android线程中进行消息处理。handler其实可以看做是一个工具类,用来向消息队列中插入消息的。

(1) Looper类用来为一个线程开启一个消息循环。

默认情况下android中新诞生的线程是没有开启消息循环的。(主线程除外,主线程系统会自动为其创建Looper对象,开启消息循环。)

Looper对象通过MessageQueue来存放消息和事件。一个线程只能有一个Looper,对应一个MessageQueue。

(2) 通常是通过Handler对象来与Looper进行交互的。Handler可看做是Looper的一个接口,用来向指定的Looper发送消息及定义处理方法。

默认情况下Handler会与其被定义时所在线程的Looper绑定,比如,Handler在主线程中定义,那么它是与主线程的Looper绑定。

mainHandler = new Handler() 等价于new Handler(Looper.myLooper()).

Looper.myLooper():获取当前进程的looper对象,类似的 Looper.getMainLooper() 用于获取主线程的Looper对象。

(3) 在非主线程中直接new Handler() 会报如下的错误:

E/AndroidRuntime( 6173): Uncaught handler: thread Thread-8 exiting due to uncaught exception

E/AndroidRuntime( 6173): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

原因是非主线程中默认没有创建Looper对象,需要先调用Looper.prepare()启用Looper。

(4) Looper.loop(); 让Looper开始工作,从消息队列里取消息,处理消息。

注意:写在Looper.loop()之后的代码不会被执行,这个函数内部应该是一个循环,当调用mHandler.getLooper().quit()后,loop才会中止,其后的代码才能得以运行。

(5) 基于以上知识,可实现主线程给子线程(非主线程)发送消息。

将代码中的mHandler声明成类成员,在主线程通过mHandler发送消息即可。

 

Handler

  Handler在android里负责发送和处理消息。它的主要用途有:

  1)按计划发送消息或执行某个Runnanble(使用POST方法);

  2)从其他线程中发送来的消息放入消息队列中,避免线程冲突(常见于更新UI线程)

   默认情况下,Handler接受的是当前线程下的消息循环实例(使用Handler(Looper looper)、Handler(Looper looper, Handler.Callback callback)可以指定线程),同时一个消息队列可以被当前线程中的多个对象进行分发、处理(在UI线程中,系统已经有一个Activity来处理了,你可以再起若干个Handler来处理)。在实例化Handler的时候,Looper可以是任意线程的,只要有Handler的指针,任何线程也都可以sendMessage。Handler对于Message的处理不是并发的。一个Looper 只有处理完一条Message才会读取下一条,所以消息的处理是阻塞形式的(handleMessage()方法里不应该有耗时操作,可以将耗时操作放在其他线程执行,操作完后发送Message(通过sendMessges方法),然后由handleMessage()更新UI)。

    调度处理消息是通过调用post(Runnable), postAtTime(Runnable, long), postDelayed(Runnable, long), sendEmptyMessage(int), sendMessage(Message), sendMessageAtTime(Message, long)和sendMessageDelayed(Message,long)等方法完成的。其中的post版本的方法可以让你将Runnable对象放进消息队列;sendMessage版本的方法可以让你将一个包含有bundle对象的消息对象放进消息队列,然后交由handleMessage(Message)方法处理。(这个需要你复写Handler的handleMessage方法)。

 

1.Handler使用post方法

View Code

程序运行结果为:

       从结果可以看出,使用post方法是将一个Runnable对象依附在主线程上面,并没有开启一个新的线程。注意:如果在Runnable对象中执行耗时的操作的话,还是会弹出ANR对话框。

 

2.Handler使用sendMessage方法

    sendMessage有多种方式:

(一)     Handler和sendMessage都在同一个线程里面

(二)     Handler在主线程,sendMessage在子线程

(三)     Handler在子线程,sendMessage在主线程

对于第一种情况,就是在创建Handler对象的时候重写handleMessage()方法,还是使用的是主线程,所以在处理消息中不能有耗时操作,否则会出现ANR对话框。

对于第二种情况,创建了一个新的线程,可以处理耗时操作,示例代码如下:

View Code

  对于第三种情况,就要涉及到HandlerThread。由于在默认情况下,Handler接受的是当前线程下的消息循环实例,要使用子线程的消息队列,就得使用HandlerThread,它继承于Thread,与普通Thread的差别在于,它有一个Looper成员变量,这个Looper其实就是对消息队列以及队列处理逻辑的封装,简单说就是消息队列+消息循环。(当我们需要一个工作线程时,而不是把它当作一次性消耗品,用过即废弃的话,就可以使用HandlerThread)。

 

HandlerThread

  Android官方文档中Looper的介绍:

   Handy class for starting a new thread that has a looper. The looper can then be used to create handler classes. Note that start() must still be called. 

HandlerThread源代码中的run方法代码如下:

  1. public void run() {
  2.         mTid = Process.myTid();
  3.         Looper.prepare();  //使该线程拥有一个Looper对象
  4.         synchronized (this) {
  5.             mLooper = Looper.myLooper();
  6.             notifyAll();
  7.         }
  8.         Process.setThreadPriority(mPriority);
  9.         onLooperPrepared();
  10.         Looper.loop();  //让Looper开始工作,从消息队列里取消息,处理消息
  11.         mTid = -1;
  12. }

继承了该类的Thread就拥有了Looper成员变量,可以处理Handler发送的消息。

View Code                                                                                                                                  

程序运行结果如下:

 

 

原创粉丝点击