Android消息机制分析

来源:互联网 发布:网络教育如何考试 编辑:程序博客网 时间:2024/06/05 03:46

对Android开发有一定了解的人都知道,Android应用程序是消息驱动的。Android应用程序的消息机制是围绕消息队列来实现的,具体的,主要通过HandlerMessageQueueLooper三个类来实现。其中Handler用来发送和处理消息;MessageQueue表示一个消息队列,负责入队和出队消息;Looper类用于创建消息循环。本文结合源码来介绍Android的消息机制包括:消息循环的创建、消息的发送和处理。

    每个Android应用程序都一个主线程ActivityThread,每个主线程都有一个消息循环。先看一下ActivityThreadmain方法:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public static void main(String[] args) {  
  2.     SamplingProfilerIntegration.start();  
  3.   
  4.     // CloseGuard defaults to true and can be quite spammy.  We  
  5.     // disable it here, but selectively enable it later (via  
  6.     // StrictMode) on debug builds, but using DropBox, not logs.  
  7.     CloseGuard.setEnabled(false);  
  8.   
  9.     Environment.initForCurrentUser();  
  10.   
  11.     // Set the reporter for event logging in libcore  
  12.     EventLogger.setReporter(new EventLoggingReporter());  
  13.   
  14.     Security.addProvider(new AndroidKeyStoreProvider());  
  15.   
  16.     // Make sure TrustedCertificateStore looks in the right place for CA certificates  
  17.     final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());  
  18.     TrustedCertificateStore.setDefaultUserDirectory(configDir);  
  19.   
  20.     Process.setArgV0("<pre-initialized>");  
  21.   
  22.     Looper.prepareMainLooper();  
  23.   
  24.     ActivityThread thread = new ActivityThread();  
  25.     thread.attach(false);  
  26.   
  27.     if (sMainThreadHandler == null) {  
  28.         sMainThreadHandler = thread.getHandler();  
  29.     }  
  30.   
  31.     if (false) {  
  32.         Looper.myLooper().setMessageLogging(new  
  33.                 LogPrinter(Log.DEBUG, "ActivityThread"));  
  34.     }  
  35.   
  36.     Looper.loop();  
  37.   
  38.     throw new RuntimeException("Main thread loop unexpectedly exited");  
  39. }  

    可以看到第22行调用Looper.prepareMainLooper(),再看一下prepareMainLooper具体做了哪些工作:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public static void prepareMainLooper() {  
  2.        prepare();  
  3.        setMainLooper(myLooper());  
  4.        myLooper().mQueue.mQuitAllowed = false;  
  5.    }  

    可以看到在第调用prepare,然后调用setMainLooper设置主线程的Looper。不难推测,prepare中会创建Looper,先贴上代码:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public static void prepare() {  
  2.         if (sThreadLocal.get() != null) {  
  3.             throw new RuntimeException("Only one Looper may be created per thread");  
  4.         }  
  5.         sThreadLocal.set(new Looper());  
  6.     }  

    prepare()里面先检查是否已经创建了Looper,如果没有,则创建一个并保存到sThreadLocal中,反之,如果已经创建了一个,则会抛出异常。通过这里,我们也可以发现,prepare只能调用一次,每个线程只有一个Looper。那这里已经创建了Looper,那么第36行的Looper.loop()又是做什么的呢?继续看源码:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public static void loop() {  
  2.       Looper me = myLooper();  
  3.       if (me == null) {  
  4.           throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");  
  5.       }  
  6.       MessageQueue queue = me.mQueue;  
  7.   
  8.       // Make sure the identity of this thread is that of the local process,  
  9.       // and keep track of what that identity token actually is.  
  10.       Binder.clearCallingIdentity();  
  11.       final long ident = Binder.clearCallingIdentity();  
  12.   
  13.       while (true) {  
  14.           Message msg = queue.next(); // might block  
  15.           if (msg != null) {  
  16.               if (msg.target == null) {  
  17.                   // No target is a magic identifier for the quit message.  
  18.                   return;  
  19.               }  
  20.   
  21.               long wallStart = 0;  
  22.               long threadStart = 0;  
  23.   
  24.               // This must be in a local variable, in case a UI event sets the logger  
  25.               Printer logging = me.mLogging;  
  26.               if (logging != null) {  
  27.                   logging.println(">>>>> Dispatching to " + msg.target + " " +  
  28.                           msg.callback + ": " + msg.what);  
  29.                   wallStart = SystemClock.currentTimeMicro();  
  30.                   threadStart = SystemClock.currentThreadTimeMicro();  
  31.               }  
  32.   
  33.               msg.target.dispatchMessage(msg);  
  34.   
  35.               if (logging != null) {  
  36.                   long wallTime = SystemClock.currentTimeMicro() - wallStart;  
  37.                   long threadTime = SystemClock.currentThreadTimeMicro() - threadStart;  
  38.   
  39.                   logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);  
  40.                   if (logging instanceof Profiler) {  
  41.                       ((Profiler) logging).profile(msg, wallStart, wallTime,  
  42.                               threadStart, threadTime);  
  43.                   }  
  44.               }  
  45.   
  46.               // Make sure that during the course of dispatching the  
  47.               // identity of the thread wasn't corrupted.  
  48.               final long newIdent = Binder.clearCallingIdentity();  
  49.               if (ident != newIdent) {  
  50.                   Log.wtf(TAG, "Thread identity changed from 0x"  
  51.                           + Long.toHexString(ident) + " to 0x"  
  52.                           + Long.toHexString(newIdent) + " while dispatching to "  
  53.                           + msg.target.getClass().getName() + " "  
  54.                           + msg.callback + " what=" + msg.what);  
  55.               }  
  56.   
  57.               msg.recycle();  
  58.           }  
  59.       }  
  60.   }  

    可以看到,在loop中会进入一个死循环,每次循环的开始调用MessageQueu.next()方法从消息队列中取出一个Message,然后调用 msg.target.dispatchMessage(msg)交给message的接受者处理。这里的msg.target就是message的接受者,其实就是一个Handler。如果消息队列为空,那么MessageQueue .next()会阻塞,直到有新的消息达到。到这里,就可以明白调用Looper.loop后,线程就进入了消息循环等待message到来并交给message对应的Handler来处理消息。正常情况下,一旦进入消息循环就不会退出,除非MessageMessage.target=null,见第16行代码。

   到此为止,我们就知道如果要给线程创建一个消息循环,可以按如下方式来定义:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. class LooperThread extends Thread {  
  2.   
  3.        public void run() {  
  4.            Looper.prepare();  
  5.   
  6.            ......  
  7.   
  8.            Looper.loop();  
  9.        }  
  10.    }  

    上面提到在Looper中会通过msg.target.dispatchMessage(msg)将msg分派到对应的Handler来出来,这里看一下dispatchMessage的实现:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public void 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.     }  

    在dispatchMessage中先检查在Message中是否定义了消息的callback,如果有,优先调用Messagecallback来处理消息;反之,如果HandlermCallBack不为null,则调用mCallBackhandleMessage来处理,当MCallBack也为null的时候,才调用HanddlerhandleMessage来处理。这里可以看出,Message有三种方式来实现它的处理逻辑:

1)在Message.callback中实现,这里的callback是一个Runnable对象;

2)在Handler的内部接口CallBack中实现;

3)重写Handler的handleMessage来实现。

    阅读Handler的源码,可以发现当调用handler.post(Runnable)的时候,就是通过第1种方式来实现:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public final boolean post(Runnable r) {  
  2.         return  sendMessageDelayed(getPostMessage(r), 0);  
  3.     }  
  4.   
  5.     private final Message getPostMessage(Runnable r) {  
  6.         Message m = Message.obtain();  
  7.         m.callback = r;  
  8.         return m;  
  9.     }  

    通常我们会通过第3种方式来定义自己的Handler,如在主线程:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. Handler mHandler=new Handler(){  
  2.   
  3.         @Override  
  4.         public void handleMessage(Message msg) {  
  5.             // TODO Auto-generated method stub  
  6.             super.handleMessage(msg);  
  7.             ......//你的处理逻辑  
  8.         }  
  9.     };  

    这里看一下Handler的构造函数(还有其他几种构造函数):

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public Handler() {  
  2.         if (FIND_POTENTIAL_LEAKS) {  
  3.             final Class<? extends Handler> klass = getClass();  
  4.             if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&  
  5.                     (klass.getModifiers() & Modifier.STATIC) == 0) {  
  6.                 Log.w(TAG, "The following Handler class should be static or leaks might occur: " +  
  7.                         klass.getCanonicalName());  
  8.             }  
  9.         }  
  10.   
  11.         mLooper = Looper.myLooper();  
  12.         if (mLooper == null) {  
  13.             throw new RuntimeException(  
  14.                     "Can't create handler inside thread that has not called Looper.prepare()");  
  15.         }  
  16.         mQueue = mLooper.mQueue;  
  17.         mCallback = null;  
  18.     }  

这里可以看到,创建Handler的时候,会在保存当前线程的Looper及Looper对应的MessageQueue。这样发送消息时Handler就能将Message发送到当前线程的消息循环中。当然,我们也可以指定Looper,这样就可以给另一个线程发送消息,如在另一个线程中给主线程发送消息。这里也可以看出,定义Handler之前一定要先调用Looper.prepare()来创建一个Looper,主线程中会在ActivityThread的main方法中创建,不需要我们手动调用。但是在子线程中,一定要先调用Looper.prepare()才能创建 Handler,否则会抛出一个异常,如第78行所示。

Handler常见的用法之一就是在子线程中更新UI,我们可以通过handler.post(Runnable)让runnable在主线程中执行。其中Activity的runOnUIThread就是利用handler来实现的,view.post(Runnable)也是相似的实现。

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public final void runOnUiThread(Runnable action) {  
  2.         if (Thread.currentThread() != mUiThread) {  
  3.             mHandler.post(action);  
  4.         } else {  
  5.             action.run();  
  6.         }  
  7.     }  

当调用handler.sendMessage()发送消息时,最终会调用Handler.sendMessageAtTime,源码如下:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public boolean sendMessageAtTime(Message msg, long uptimeMillis)  
  2.    {  
  3.        boolean sent = false;  
  4.        MessageQueue queue = mQueue;  
  5.        if (queue != null) {  
  6.            msg.target = this;  
  7.            sent = queue.enqueueMessage(msg, uptimeMillis);  
  8.        }  
  9.        else {  
  10.            RuntimeException e = new RuntimeException(  
  11.                    this + " sendMessageAtTime() called with no mQueue");  
  12.            Log.w("Looper", e.getMessage(), e);  
  13.        }  
  14.        return sent;  
  15.    }  

    在这里可以看出,发送消息的时候,将Message添加到队列中,并且会将Message的接受者设置为发送Message的handler,如代码第6行所示。前面有提到,在loop中会调用MessageQueue.next()来取消息。这里先看一下消息的入队操作:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. final boolean enqueueMessage(Message msg, long when) {  
  2.         if (msg.isInUse()) {  
  3.             throw new AndroidRuntimeException(msg  
  4.                     + " This message is already in use.");  
  5.         }  
  6.         if (msg.target == null && !mQuitAllowed) {  
  7.             throw new RuntimeException("Main thread not allowed to quit");  
  8.         }  
  9.         final boolean needWake;  
  10.         synchronized (this) {  
  11.             if (mQuiting) {  
  12.                 RuntimeException e = new RuntimeException(  
  13.                         msg.target + " sending message to a Handler on a dead thread");  
  14.                 Log.w("MessageQueue", e.getMessage(), e);  
  15.                 return false;  
  16.             } else if (msg.target == null) {  
  17.                 mQuiting = true;  
  18.             }  
  19.   
  20.             msg.when = when;  
  21.             //Log.d("MessageQueue", "Enqueing: " + msg);  
  22.             Message p = mMessages;  
  23.             if (p == null || when == 0 || when < p.when) {  
  24.                 msg.next = p;  
  25.                 mMessages = msg;  
  26.                 needWake = mBlocked; // new head, might need to wake up  
  27.             } else {  
  28.                 Message prev = null;  
  29.                 while (p != null && p.when <= when) {  
  30.                     prev = p;  
  31.                     p = p.next;  
  32.                 }  
  33.                 msg.next = prev.next;  
  34.                 prev.next = msg;  
  35.                 needWake = false// still waiting on head, no need to wake up  
  36.             }  
  37.         }  
  38.         if (needWake) {  
  39.             nativeWake(mPtr);  
  40.         }  
  41.         return true;  
  42.     }  

    看代码的第2935行代码,这里会根据消息的处理时间Message.when来确定消息插入到队列中的具体位置,从而保证队首的消息是下一个要处理的。在这里也可以发现,主线程的消息循环是不能退出的,如第6行所示。

前面有提到MessageQueue.next()可能会阻塞。

    这里一起看一下消息的出队操作:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. final Message next() {  
  2.         int pendingIdleHandlerCount = -1// -1 only during first iteration  
  3.         int nextPollTimeoutMillis = 0;  
  4.   
  5.         for (;;) {  
  6.             if (nextPollTimeoutMillis != 0) {  
  7.                 Binder.flushPendingCommands();  
  8.             }  
  9.             nativePollOnce(mPtr, nextPollTimeoutMillis);  
  10.   
  11.             synchronized (this) {  
  12.                 // Try to retrieve the next message.  Return if found.  
  13.                 final long now = SystemClock.uptimeMillis();  
  14.                 final Message msg = mMessages;  
  15.                 if (msg != null) {  
  16.                     final long when = msg.when;  
  17.                     if (now >= when) {  
  18.                         mBlocked = false;  
  19.                         mMessages = msg.next;  
  20.                         msg.next = null;  
  21.                         if (false) Log.v("MessageQueue""Returning message: " + msg);  
  22.                         msg.markInUse();  
  23.                         return msg;  
  24.                     } else {  
  25.                         nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);  
  26.                     }  
  27.                 } else {  
  28.                     nextPollTimeoutMillis = -1;  
  29.                 }  
  30.   
  31.                 // If first time, then get the number of idlers to run.  
  32.                 if (pendingIdleHandlerCount < 0) {  
  33.                     pendingIdleHandlerCount = mIdleHandlers.size();  
  34.                 }  
  35.                 if (pendingIdleHandlerCount == 0) {  
  36.                     // No idle handlers to run.  Loop and wait some more.  
  37.                     mBlocked = true;  
  38.                     continue;  
  39.                 }  
  40.   
  41.                 if (mPendingIdleHandlers == null) {  
  42.                     mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];  
  43.                 }  
  44.                 mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);  
  45.             }  
  46.   
  47.             // Run the idle handlers.  
  48.             // We only ever reach this code block during the first iteration.  
  49.             for (int i = 0; i < pendingIdleHandlerCount; i++) {  
  50.                 final IdleHandler idler = mPendingIdleHandlers[i];  
  51.                 mPendingIdleHandlers[i] = null// release the reference to the handler  
  52.   
  53.                 boolean keep = false;  
  54.                 try {  
  55.                     keep = idler.queueIdle();  
  56.                 } catch (Throwable t) {  
  57.                     Log.wtf("MessageQueue""IdleHandler threw exception", t);  
  58.                 }  
  59.   
  60.                 if (!keep) {  
  61.                     synchronized (this) {  
  62.                         mIdleHandlers.remove(idler);  
  63.                     }  
  64.                 }  
  65.             }  
  66.   
  67.             // Reset the idle handler count to 0 so we do not run them again.  
  68.             pendingIdleHandlerCount = 0;  
  69.   
  70.             // While calling an idle handler, a new message could have been delivered  
  71.             // so go back and look again for a pending message without waiting.  
  72.             nextPollTimeoutMillis = 0;  
  73.         }  
  74.     }  

    在第3行,定义变量nextPollTimeoutMills,用来描述当消息队列中没有新的消息时,当前线程需要睡眠多长时间。当nextPollTimeoutMills等于-1时,表示无限制等待,直到有新的消息到达。在没有消息到达的时候会调用nativePollOnce来检查是否有新消息需要处理,nativePollOnce是一个native函数,主要是通过Linux中的poll函数来实现的。

    当队列不为空时,在第17行先判断当前时间是否大于等于message的处理时间,如果是,则返回该message,反之,计算需要等待的时间,并更新nextPollTimeoutMills

    如果队列为空,则nextPollTimeoutMills的值设置为-1。在进入睡眠之前,先调用注册到消息队列中的空闲消息处理器IdleHandler对象的queueIdle函数,以便有机会在线程空闲时执行一些操作。可以通过MessageQueueaddIdleHandlerremoveIdleHandler来注册和注销一个空闲消息处理器。

   到此为止,已经分析完消息循环的创建,发送消息,以及消息的处理。

0 0