Android 消息机制 Handler 原理解析

几乎所有的发送消息的方法最后都调用了 sendMessageAtTime  方法,在这个发放中判断了是否存在MessageQueue对象之后调用了 enqueueMessage  方法,在这个方法中,把 msg.targt  设置为此Handler对象,注意,这里是用于之后在MessageQueue里面消费消息时找到对应的处理此消息的Handler,然后调用MessageQueue的 enqueueMessage()  方法,此方法主要是用于把消息放入MessageQueue里面,即入队操作。

1 boolean   enqueueMessage (Message msg,  long  when)   {

2 if (msg.target ==  null ) {

3 throw new IllegalArgumentException( “Message must have a target.” );

4 }

5 if (msg.isInUse()) {

6 throw new IllegalStateException(msg +  ” This message is already in use.” );

7 }

8

9 synchronized ( this ) {

10 if (mQuitting) {

11 IllegalStateException e =  new IllegalStateException(

12 msg.target +  ” sending message to a Handler on a dead thread” );

13 Log.w(TAG, e.getMessage(), e);

14 msg.recycle();

15 return false ;

16 }

17

18 msg.markInUse();

19 msg.when = when;

20 Message p = mMessages;

21 boolean needWake;

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

23 // New head, wake up the event queue if blocked.

24 msg.next = p;

25 mMessages = msg;

26 needWake = mBlocked;

27else {

28

29

30 // and the message is the earliest asynchronous message in the queue.

31 needWake = mBlocked && p.target ==  null && msg.isAsynchronous();

32 Message prev;

33 for (;;) {

34 prev = p;

35 p = p.next;

36 if (p ==  null || when < p.when) {

37 break ;

38 }

39 if (needWake && p.isAsynchronous()) {

40 needWake =  false ;

41 }

42 }

43 msg.next = p;  // invariant: p == prev.next

44 prev.next = msg;

45 }

46

47 // We can assume mPtr != 0 because mQuitting is false.

48 if (needWake) {

49 nativeWake(mPtr);

50 }

51 }

52 return true ;

53

}

Message消息通过MessageQueue的 enqueueMessage  方法进行入队操作,在此方法中先判断 msg.target  是否存在,当前消息是否在使用中,是否收到退出信息等的检测,全部正常才进行入队,从上面代码我们可以看到,MessageQueue是通过类似于链表的形式存储Message消息的,且是通过消费时间进行排序的,通过前面我们可以知道,入队用于排序的时间是Message的消费时间,即 System.currentTime()+delayMillis  ;循环遍历消息队列,找到 msg.when  应该插入的地方,插入链表,完成消息的入队操作。 这里还需要注意的是 nativeWake(mPtr)  ;方法,这个方法是用通过JNI实现的,即在底层通过C实现的,然后通过JNI调用,在底层中维持了一个 mWakeEventFd  文件,这个文件是专门用于进程和线程之间通信的,并且通过 epoll  监控。 这里在入队完成之后会调用此方法,通过此方法会向 MwakeEventFD  文件写入一个 uint64_t  ,这个是用于唤醒消息循环线程的,在后面消息取出的时候进行详细说明。

通过上面的方法就完成了消息发送过程,接下来则是消息循环过程,当消息的消费时间到了之后取出相应的消息进行消费。通过上面的时候我们知道在线程中使用完Handler之后需要调用Looper.loop()完成整个过程,在主线程不需要我们操作是因为ActivityThread里面已经调用过了,在 loop()  里面做的就是消息的阻塞等待。

1 public   static   void   loop ()   {

2 final Looper me = myLooper();

3 if (me ==  null ) {

4 throw new RuntimeException( “No Looper; Looper.prepare() wasn’t called on this thread.” );

5 }

6 final MessageQueue queue = me.mQueue;

7

8

9 // and keep track of what that identity token actually is.

10 Binder.clearCallingIdentity();

11 final long ident = Binder.clearCallingIdentity();

12

13 for (;;) {

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

15 if (msg ==  null ) {

16 // No message indicates that the message queue is quitting.

17 return ;

18 }

19

20

21 final Printer logging = me.mLogging;

22 if (logging !=  null ) {

23 logging.println( “>>>>> Dispatching to “ + msg.target +  ” “ +

24 msg.callback +  “: “ + msg.what);

25 }

26

27 final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;

28

29 final long traceTag = me.mTraceTag;

30 if (traceTag !=  0 && Trace.isTagEnabled(traceTag)) {

31 Trace.traceBegin(traceTag, msg.target.getTraceName(msg));

32 }

33 final long start = (slowDispatchThresholdMs ==  0 ) ?  0 : SystemClock.uptimeMillis();

34 final long end;

35 try {

36 msg.target.dispatchMessage(msg);

37 end = (slowDispatchThresholdMs ==  0 ) ?  0 : SystemClock.uptimeMillis();

38finally {

39 if (traceTag !=  0 ) {

40 Trace.traceEnd(traceTag);

41 }

42 }

43 if (slowDispatchThresholdMs >  0 ) {

44 final long time = end – start;

45 if (time > slowDispatchThresholdMs) {

46 Slog.w(TAG,  “Dispatch took “ + time +  “ms on “

47 + Thread.currentThread().getName() +  “, h=” +

48 msg.target +  ” cb=” + msg.callback +  ” msg=” + msg.what);

49 }

50 }

51

52 if (logging !=  null ) {

53 logging.println( “<<<<< Finished to " + msg.target +  ” “ + msg.callback);

54 }

55

56 // Make sure that during the course of dispatching the

57 // identity of the thread wasn’t corrupted.

58 final long newIdent = Binder.clearCallingIdentity();

59 if (ident != newIdent) {

60 Log.wtf(TAG,  “Thread identity changed from 0x”

61 + Long.toHexString(ident) +  ” to 0x”

62 + Long.toHexString(newIdent) +  ” while dispatching to “

63 + msg.target.getClass().getName() +  ” “

64 + msg.callback +  ” what=” + msg.what);

65 }

66

67 msg.recycleUnchecked();

68 }

69

}

从代码中我们可以看出,这是个无限循环方法等待获取可以处理的消息,在循环里面,通过 do-while  找到一个不为空且是asynchronous的消息,找到之后检测这个消息是否到了执行时间,如果不到的话,通过 Math.min(msg.when – now, Integer.MAX_VALUE)  ;设置等待时间,如果这个消息找到了,且到了执行时间了,那么就取出此消息,并把链表中此消息删除,把这个消息通过msg.markInUse();标志为使用中,把这个找到的消息返回给Looper的loop方法,进行消息分发。 如果到了链表尾部还是没有符合的消息,则进入阻塞等待过程。

另外在此方法中还得注意 nativePollOnce(ptr, nextPollTimeoutMillis)  ;这个方法也是JNI调用C++实现的。 之前说到在通过Handler发送消息的时候,会向mWakeEventFd文件写一个 uint_64  ,而 nativePollOnce  方法则阻塞监听mWakeEventFd文件以及唤醒消息循环线程的。 当有消息发送的时候就会写入一个 uint_64  ,而 nativePollOnce  里面则是一直监听这个文件,当有写入操作发生时,就会唤醒 epoll_ wait 即消息循环线程,线程就会去取出队列里面的消息去执行操作,当队列里面没有消息的时候,又会继续等待,当下次有信息写入的时候则再次唤醒线程去取出队列消息。 这样就完成一次消息循环。

继续看上面的取出消息之后的处理,上面说到取出消息之后loop()方法是调用了msg.target.dispatchMessage方法进行消息处理,而msg.target是之前设置的目标Handler,现在去Handler里面看看dispatchMessage的处理:

1 public   void   dispatchMessage (Message msg)   {

2 if (msg.callback !=  null ) {

3 handleCallback(msg);

4else {

5 if (mCallback !=  null ) {

6 if (mCallback.handleMessage(msg)) {

7 return ;

8 }

9 }

10 handleMessage(msg);

11 }

12

}

这里就是如何处理信息了,如果为Message设置了callback的话则直接 message.callback.run()  处理消息,如果有设置 Handler的callback  ,也进行分发; 如果都没有的话,那就直接调用 handleMessage(msg) ;还记得我们最开是的时候写的简单使用Handler的例子么,里面重写了一个方法,就是handleMessage(msg);所以到这里就清楚了,前面发出的消息就是在这里进行处理的。 另外还有很多经常用的方法都是通过包装的Handler来进行的,比如:

1. Activity.runOnUiThread(Runnable action)

1 public   final   void   runOnUiThread (Runnable action)   {

2 if (Thread.currentThread() != mUiThread) {

3 mHandler.post(action);

4else {

5 action.run();

6 }

7

}

这个是Activity的方法,用于在切换到主线程运行,可以看到内部实现是判断当前线程是否是主线程,如果是的话直接运行,不是的话用 mHandler.post 发送出去,而mHandler是申明在主线程的Handler,经过发送之后再主线程的Handler里面完成调用。

2. View.post() 

1 public   boolean   post (Runnable action)   {

2 final AttachInfo attachInfo = mAttachInfo;

3 if (attachInfo !=  null ) {

4 return attachInfo.mHandler.post(action);

5 }

6

7

8 // Assume that the runnable will be successfully placed after attach.

9 getRunQueue().post(action);

10 return true ;

11

}

View.pos t() 用于异步更新view,也是找到主线程Handler,然后通过Handler发送消息,在主线程更新Handler。另外还有很多经典方法都是用Handler更新数据的,最典型的是Android官方提供的异步工具AsyncTask,用它可以进行异步请求或者异步下载数据,下载图片等等,AsyncTask就是用Handler实现的多线程异步工具,开启子线程进行数据请求,然后通过Handler发送数据消息,最好在主线程里面处理收到的消息,完成异步请求。