Android中的Handler, Looper, MessageQueue和Thread

Android中的消息机制,消息的发送和接收过程以及与线程之间的关系。虽然我们经常使用这些基础的东西,但对于其内部原理的了解,能使我们更加容易、合理地架构系统,并避免一些低级错误。

对于这部分的内容,将分成4小节来描述:

1.职责与关系

2.消息循环

3.线程与更新

4.几点小结

--------------------------------------------------------------------------------------------------

1) 接下来,我们开始这部分的内容,首先了解一下各自的职责及相互之间的关系。

职责

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,Looper和MessageQueue就是简单的三角关系。Looper和MessageQueue一一对应,创建一个Looper的同时,会创建一个MessageQueue。而Handler与它们的关系,只是简单的聚集关系,即Handler里会引用当前线程里的特定Looper和MessageQueue。

这样说来,多个Handler都可以共享同一Looper和MessageQueue了。当然,这些Handler也就运行在同一个线程里。

2) 接下来,我们简单地看一下消息的循环过程:

生成

Message msg =mHandler.obtainMessage();

msg.what = what;

msg.sendToTarget();

发送

MessageQueue queue = mQueue;

if (queue != null) {

msg.target = this;

sent =queue.enqueueMessage(msg, uptimeMillis);

}

在Handler.java的sendMessageAtTime(Message msg, long uptimeMillis)方法中,我们看到,它找到它所引用的MessageQueue,然后将Message的target设定成自己(目的是为了在处理消息环节,Message能找到正确的Handler),再将这个Message纳入到消息队列中。

抽取

Looper me = myLooper();

MessageQueue queue = me.mQueue;

while (true) {

Messagemsg = queue.next(); // might block

if (msg!= null) {

if (msg.target == null) {

// No target is a magic identifier for the quit message.

return;

}

msg.target.dispatchMessage(msg);

msg.recycle();

}

}

在Looper.java的loop()函数里,我们看到,这里有一个死循环,不断地从MessageQueue中获取下一个(next方法)Message,然后通过Message中携带的target信息,交由正确的Handler处理(dispatchMessage方法)。

处理

if (msg.callback != null) {

handleCallback(msg);

} else {

if(mCallback != null) {

if (mCallback.handleMessage(msg)) {

return;

}

}

handleMessage(msg);

}

在Handler.java的dispatchMessage(Message msg)方法里,其中的一个分支就是调用handleMessage方法来处理这条Message,而这也正是我们在职责处描述使用Handler时需要实现handleMessage(Message msg)的原因。

至于dispatchMessage方法中的另外一个分支,我将会在后面的内容中说明。

至此,我们看到,一个Message经由Handler的发送,MessageQueue的入队,Looper的抽取,又再一次地回到Handler的怀抱。而绕的这一圈,也正好帮助我们将同步操作变成了异步操作。

3)剩下的部分,我们将讨论一下Handler所处的线程及更新UI的方式。

在主线程(UI线程)里,如果创建Handler时不传入Looper对象,那么将直接使用主线程(UI线程)的Looper对象(系统已经帮我们创建了);在其它线程里,如果创建Handler时不传入Looper对象,那么,这个Handler将不能接收处理消息。在这种情况下,通用的作法是:

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(Looper.prepare),然后让这个Looper跑起来(Looper.loop),抽取Message,这样,Handler才能正常工作。

因此,Handler处理消息总是在创建Handler的线程里运行。而我们的消息处理中,不乏更新UI的操作,不正确的线程直接更新UI将引发异常。因此,需要时刻关心Handler在哪个线程里创建的。

如何更新UI才能不出异常呢?SDK告诉我们,有以下4种方式可以从其它线程访问UI线程:

·     Activity.runOnUiThread(Runnable)

·     View.post(Runnable)

·     View.postDelayed(Runnable, long)

·     Handler

其中,重点说一下的是View.post(Runnable)方法。在post(Runnable action)方法里,View获得当前线程(即UI线程)的Handler,然后将action对象post到Handler里。在Handler里,它将传递过来的action对象包装成一个Message(Message的callback为action),然后将其投入UI线程的消息循环中。在Handler再次处理该Message时,有一条分支(未解释的那条)就是为它所设,直接调用runnable的run方法。而此时,已经路由到UI线程里,因此,我们可以毫无顾虑的来更新UI。

4) 几点小结

·      Handler的处理过程运行在创建Handler的线程里

·      一个Looper对应一个MessageQueue

·      一个线程对应一个Looper

·      一个Looper可以对应多个Handler

·      不确定当前线程时,更新UI时尽量调用post方法

 xirihanlin 2011.01.30




深入剖析Android消息机制

[日期:2011-04-26]         来源:Linux社区  作者:coolszy


在Android中,线程内部或者线程之间进行信息交互时经常会使用消息,这些基础的东西如果我们熟悉其内部的原理,将会使我们容易、更好地架构系统,避免一些低级的错误。在学习Android中消息机制之前,我们先了解与消息有关的几个类:

1.Message

消息对象,顾名思义就是记录消息信息的类。这个类有几个比较重要的字段:

a.arg1和arg2:我们可以使用两个字段用来存放我们需要传递的整型值,在Service中,我们可以用来存放Service的ID。


b.obj:该字段是Object类型,我们可以让该字段传递某个多项到消息的接受者中。

c.what:这个字段可以说是消息的标志,在消息处理中,我们可以根据这个字段的不同的值进行不同的处理,类似于我们在处理Button事件时,通过switch(v.getId())判断是点击了哪个按钮。

在使用Message时,我们可以通过new Message()创建一个Message实例,但是Android更推荐我们通过Message.obtain()或者Handler.obtainMessage()获取Message对象。这并不一定是直接创建一个新的实例,而是先从消息池中看有没有可用的Message实例,存在则直接取出并返回这个实例。反之如果消息池中没有可用的Message实例,则根据给定的参数new一个新Message对象。通过分析源码可得知,Android系统默认情况下在消息池中实例化10个Message对象。

代码下载地址:

在Linux公社的1号FTP服务器里,下载地址:

FTP地址:ftp://www.linuxidc.com

用户名:www.linuxidc.com

密码:www.muu.cc

在 2011年LinuxIDC.com\4月\深入剖析Android消息机制

下载方法见 http://www.linuxidc.net/thread-1187-1-1.html

2.MessageQueue

消息队列,用来存放Message对象的数据结构,按照“先进先出”的原则存放消息。存放并非实际意义的保存,而是将Message对象以链表的方式串联起来的。MessageQueue对象不需要我们自己创建,而是有Looper对象对其进行管理,一个线程最多只可以拥有一个MessageQueue。我们可以通过Looper.myQueue()获取当前线程中的MessageQueue。

3.Looper

MessageQueue的管理者,在一个线程中,如果存在Looper对象,则必定存在MessageQueue对象,并且只存在一个Looper对象和一个MessageQueue对象。在Android系统中,除了主线程有默认的Looper对象,其它线程默认是没有Looper对象。如果想让我们新创建的线程拥有Looper对象时,我们首先应调用Looper.prepare()方法,然后再调用Looper.loop()方法。典型的用法如下:

1.      classLooperThread extends Thread   

2.      {   

3.          public Handler mHandler;   

4.          public void run()   

5.          {   

6.              Looper.prepare();   

7.              //其它需要处理的操作   

8.              Looper.loop();   

9.          }   

10.   } 

倘若我们的线程中存在Looper对象,则我们可以通过Looper.myLooper()获取,此外我们还可以通过Looper.getMainLooper()获取当前应用系统中主线程的Looper对象。在这个地方有一点需要注意,假如Looper对象位于应用程序主线程中,则Looper.myLooper()和Looper.getMainLooper()获取的是同一个对象。

4.Handler

消息的处理者。通过Handler对象我们可以封装Message对象,然后通过sendMessage(msg)把Message对象添加到MessageQueue中;当MessageQueue循环到该Message时,就会调用该Message对象对应的handler对象的handleMessage()方法对其进行处理。由于是在handleMessage()方法中处理消息,因此我们应该编写一个类继承自Handler,然后在handleMessage()处理我们需要的操作。


1.      /** 

2.      *  

3.      * @author coolszy 

4.      * @blog http://blog.csdn.net/coolszy 

5.      * 

6.      */ 

7.       

8.      publicclass MessageService extends Service  

9.       

10.   {  

11.       private static final String TAG ="MessageService";  

12.       private static final int KUKA = 0;  

13.       private Looper looper;  

14.       private ServiceHandler handler;  

15.       /** 

16.        *由于处理消息是在Handler的handleMessage()方法中,因此我们需要自己编写类 

17.        *继承自Handler类,然后在handleMessage()中编写我们所需要的功能代码 

18.        * @author coolszy 

19.        * 

20.        */ 

21.       private final class ServiceHandler extendsHandler  

22.       {  

23.           public ServiceHandler(Looperlooper)  

24.           {  

25.               super(looper);  

26.           }  

27.    

28.           @Override 

29.           public void handleMessage(Messagemsg)  

30.           {  

31.               //根据what字段判断是哪个消息   

32.               switch (msg.what)  

33.               {  

34.               case KUKA:  

35.                   //获取msg的obj字段。我们可在此编写我们所需要的功能代码  

36.                   Log.i(TAG, "The obj fieldof msg:" + msg.obj);  

37.                   break;  

38.               // other cases  

39.               default:  

40.                   break;  

41.               }  

42.               //如果我们Service已完成任务,则停止Service  

43.               stopSelf(msg.arg1);  

44.           }  

45.       }   

46.    

47.       @Override 

48.       public void onCreate()  

49.       {  

50.           Log.i(TAG,"MessageService-->onCreate()");  

51.           //默认情况下Service是运行在主线程中,而服务一般又十分耗费时间,如果  

52.           //放在主线程中,将会影响程序与用户的交互,因此把Service  

53.           //放在一个单独的线程中执行  

54.           HandlerThread thread = newHandlerThread("MessageDemoThread",Process.THREAD_PRIORITY_BACKGROUND);  

55.           thread.start();  

56.           //获取当前线程中的looper对象  

57.           looper = thread.getLooper();  

58.           //创建Handler对象,把looper传递过来使得handler、  

59.           //looper和messageQueue三者建立联系  

60.           handler = newServiceHandler(looper);  

61.       }  

62.    

63.       @Override 

64.       public int onStartCommand(Intent intent,int flags, int startId)  

65.       {  

66.           Log.i(TAG,"MessageService-->onStartCommand()");  

67.    

68.          //从消息池中获取一个Message实例  

69.           Message msg =handler.obtainMessage();  

70.          // arg1保存线程的ID,在handleMessage()方法中  

71.           //我们可以通过stopSelf(startId)方法,停止服务  

72.           msg.arg1 = startId;  

73.          // msg的标志  

74.           msg.what = KUKA;  

75.          //在这里我创建一个date对象,赋值给obj字段  

76.           //在实际中我们可以通过obj传递我们需要处理的对象  

77.           Date date = new Date();  

78.          msg.obj = date;  

79.          //把msg添加到MessageQueue中  

80.           handler.sendMessage(msg);  

81.          return START_STICKY;  

82.       }  

83.    

84.       @Override 

85.       public void onDestroy()  

86.       {  

87.           Log.i(TAG,"MessageService-->onDestroy()");  

88.       }  

89.      

90.       @Override 

91.       public IBinder onBind(Intent intent)  

92.       {  

93.           return null;  

94.       }  

95.   } 

下面我们通过跟踪代码分析在Android中是如何处理消息。首先贴上测试代码:

运行结果:


注:在测试代码中我们使用了HandlerThread类,该类是Thread的子类,该类运行时将会创建looper对象,使用该类省去了我们自己编写Thread子类并且创建Looper的麻烦。

下面我们分析下程序的运行过程:

1.onCreate()

首先启动服务时将会调用onCreate()方法,在该方法中我们new了一个HandlerThread对象,提供了线程的名字和优先级。

紧接着我们调用了start()方法,执行该方法将会调用HandlerThread对象的run()方法:

1.      publicvoid run() {  

2.              mTid = Process.myTid();  

3.              Looper.prepare();  

4.              synchronized (this) {  

5.                  mLooper = Looper.myLooper();  

6.                  notifyAll();  

7.              }  

8.             Process.setThreadPriority(mPriority);  

9.              onLooperPrepared();  

10.           Looper.loop();  

11.           mTid = -1;  

12.       } 

在run()方法中,系统给线程添加的Looper,同时调用了Looper的loop()方法:

1.      publicstatic final void loop() {  

2.       

3.              Looper me = myLooper();  

4.              MessageQueue queue = me.mQueue;  

5.              while (true) {  

6.                  Message msg = queue.next(); //might block  

7.                  //if (!me.mRun) {  

8.                  //    break;  

9.                  //}  

10.               if (msg != null) {  

11.                   if (msg.target == null) {  

12.                       // No target is a magicidentifier for the quit message.  

13.                       return;  

14.                   }  

15.                   if (me.mLogging!= null)me.mLogging.println(  

16.                          ">>>>> Dispatching to " + msg.target + "" 

17.                           + msg.callback +": " + msg.what  

18.                           );  

19.                  msg.target.dispatchMessage(msg);  

20.                   if (me.mLogging!= null)me.mLogging.println(   

21.                          "<<<<< Finished to   " + msg.target + " " 

22.                           + msg.callback);  

23.                   msg.recycle();  

24.               }  

25.           }  

26.       } 

通过源码我们可以看到loop()方法是个死循环,将会不停的从MessageQueue对象中获取Message对象,如果MessageQueue 对象中不存在Message对象,则结束本次循环,然后继续循环;如果存在Message对象,则执行 msg.target.dispatchMessage(msg),但是这个msg的.target字段的值是什么呢?我们先暂时停止跟踪源码,返回到onCreate()方法中。线程执行完start()方法后,我们可以获取线程的Looper对象,然后new一个ServiceHandler对象,我们把Looper对象传到ServiceHandler构造函数中将使handler、looper和messageQueue三者建立联系。

2.onStartCommand()

执行完onStart()方法后,将执行onStartCommand()方法。首先我们从消息池中获取一个Message实例,然后给Message对象的arg1、what、obj三个字段赋值。紧接着调用sendMessage(msg)方法,我们跟踪源代码,该方法将会调用sendMessageDelayed(msg, 0)方法,而sendMessageDelayed()方法又会调用sendMessageAtTime(msg,

SystemClock.uptimeMillis() + delayMillis)方法,在该方法中我们要注意该句代码msg.target

= this,msg的target指向了this,而this就是ServiceHandler对象,因此msg的target字段指向了ServiceHandler对象,同时该方法又调用MessageQueue 的enqueueMessage(msg, uptimeMillis)方法:

1.      finalboolean enqueueMessage(Message msg, long when) {  

2.              if (msg.when != 0) {  

3.                  throw newAndroidRuntimeException(msg  

4.                          + " This message isalready in use.");  

5.              }  

6.              if (msg.target == null &&!mQuitAllowed) {  

7.                  throw newRuntimeException("Main thread not allowed to quit");  

8.              }  

9.              synchronized (this) {  

10.               if (mQuiting) {  

11.                   RuntimeException e = newRuntimeException(  

12.                       msg.target + " sendingmessage to a Handler on a dead thread");  

13.                   Log.w("MessageQueue",e.getMessage(), e);  

14.                   return false;  

15.               } else if (msg.target == null){  

16.                   mQuiting = true;  

17.               }  

18.               msg.when = when;  

19.               //Log.d("MessageQueue","Enqueing: " + msg);  

20.               Message p = mMessages;  

21.               if (p == null || when == 0 || when< p.when) {  

22.                   msg.next = p;  

23.                   mMessages = msg;  

24.                   this.notify();  

25.               } else {  

26.                   Message prev = null;  

27.                   while (p != null &&p.when <= when) {  

28.                       prev = p;  

29.                       p = p.next;  

30.                   }  

31.                   msg.next = prev.next;  

32.                   prev.next = msg;  

33.                   this.notify();  

34.               }  

35.           }  

36.           return true;  

37.       } 

该方法主要的任务就是把Message对象的添加到MessageQueue中(数据结构最基础的东西,自己画图理解下)。

handler.sendMessage()-->handler.sendMessageDelayed()-->handler.sendMessageAtTime()-->msg.target

= this;queue.enqueueMessage==>把msg添加到消息队列中

3.handleMessage(msg)

onStartCommand()执行完毕后我们的Service中的方法就执行完毕了,那么handleMessage()是怎么调用的呢?在前面分析的loop()方法中,我们当时不知道msg的target字段代码什么,通过上面分析现在我们知道它代表ServiceHandler对象,msg.target.dispatchMessage(msg);则表示执行ServiceHandler对象中的dispatchMessage()方法:


1.      publicvoid dispatchMessage(Message msg) {  

2.              if (msg.callback != null) {  

3.                  handleCallback(msg);  

4.              } else {  

5.                  if (mCallback != null) {  

6.                      if(mCallback.handleMessage(msg)) {  

7.                          return;  

8.                      }  

9.                  }  

10.               handleMessage(msg);  

11.           }  

12.       }

该方法首先判断callback是否为空,我们跟踪的过程中未见给其赋值,因此callback字段为空,所以最终将会执行handleMessage()方法,也就是我们ServiceHandler类中复写的方法。在该方法将根据what字段的值判断执行哪段代码。

至此,我们看到,一个Message经由Handler的发送,MessageQueue的入队,Looper的抽取,又再一次地回到Handler的怀抱中。而绕的这一圈,也正好帮助我们将同步操作变成了异步操作。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容