请选择 进入手机版 | 继续访问电脑版

技术控

    今日:0| 主题:61300
收藏本版 (1)
最新软件应用技术尽在掌握

[其他] Handler、Looper与MessageQueue源码分析

[复制链接]
小生活大甜蜜 发表于 2016-10-19 17:18:57
347 5
在 Android 中可以通过 Handler 来更新主线程中 UI 的变化,更新 UI 只能在主线程中进行更新,而为了让其他线程也能控制 UI 的变化, Android 提供了一种机制 Handler 、 Looper 与 MessageQueue 一同协作来达到其他线程更新 UI 的目的。
   一般我们会在主线程中通过如下方法定义一个 Handler
  1. private Handler mHandler = new Handler() {
  2. @Override
  3. public void handleMessage(Message msg) {
  4. tv.setText("mHandler change UI");
  5. super.handleMessage(msg);
  6. }
  7. };
复制代码
  一般都见不到 Looper 与 MessageQueue 的,那么它们都是在哪里调用与如何协作的呢?在主线程不会显式的调用 Looper 而是会在 ActivityThread.main 方法中默认调用。
  ActivityThread.main

  1. public static void main(String[] args) {
  2. Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
  3. SamplingProfilerIntegration.start();
  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. Environment.initForCurrentUser();
  9. // Set the reporter for event logging in libcore
  10. EventLogger.setReporter(new EventLoggingReporter());
  11. // Make sure TrustedCertificateStore looks in the right place for CA certificates
  12. final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
  13. TrustedCertificateStore.setDefaultUserDirectory(configDir);
  14. Process.setArgV0("<pre-initialized>");
  15. Looper.prepareMainLooper();//创建Looper
  16. ActivityThread thread = new ActivityThread();
  17. thread.attach(false);
  18. if (sMainThreadHandler == null) {
  19. sMainThreadHandler = thread.getHandler();
  20. }
  21. if (false) {
  22. Looper.myLooper().setMessageLogging(new
  23. LogPrinter(Log.DEBUG, "ActivityThread"));
  24. }
  25. // End of event ActivityThreadMain.
  26. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
  27. Looper.loop();//开启Looper循环
  28. throw new RuntimeException("Main thread loop unexpectedly exited");
  29. }
复制代码
  如上代码,调用了 Looper.prepareMainLooper() 方法,在主线程中创建了一个 Looper ,不信的话我们再查看该方法做了什么
  Looper

  prepare

  1. public static void prepare() {
  2. prepare(true);
  3. }
  4. private static void prepare(boolean quitAllowed) {
  5. if (sThreadLocal.get() != null) {
  6. throw new RuntimeException("Only one Looper may be created per thread");
  7. }
  8. sThreadLocal.set(new Looper(quitAllowed));//创建Looper并赋给sThreadLocal
  9. }
  10. /**
  11. * Initialize the current thread as a looper, marking it as an
  12. * application's main looper. The main looper for your application
  13. * is created by the Android environment, so you should never need
  14. * to call this function yourself. See also: [email protected] #prepare()}
  15. */
  16. public static void prepareMainLooper() {
  17. prepare(false);
  18. synchronized (Looper.class) {
  19. if (sMainLooper != null) {
  20. throw new IllegalStateException("The main Looper has already been prepared.");
  21. }
  22. sMainLooper = myLooper();
  23. }
  24. }
  25. public static @Nullable Looper myLooper() {
  26. return sThreadLocal.get();
  27. }
复制代码
  在 prepareMainLooper 方法中调用了 prepare 而通过 prepare 会发现它其实就是创建了一个 Looper ,并把它赋给了 sThreadLocal 。同时可以通过 myLooper 方法获取当前线程中的 Looper 。再来看下 new Looper(quitAllowed) 初始化了什么
  1. private Looper(boolean quitAllowed) {
  2. mQueue = new MessageQueue(quitAllowed);
  3. mThread = Thread.currentThread();
  4. }
复制代码
  在这里我们终于看到了 MessageQueue 了,它创建了一个 MessageQueue 。该消息队列就是用来保存后续的 Message 。再回到 ActivityThread.main 方法中,发现它调用了 Looper.loop() 是用来开启 Looper 循环的,监听消息队列 MessageQueue 中的消息。
  loop

   我们来看下 Looper.loop() 的源码:
  1. public static void loop() {
  2. final Looper me = myLooper();//获取Looper
  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. // Make sure the identity of this thread is that of the local process,
  8. // and keep track of what that identity token actually is.
  9. Binder.clearCallingIdentity();
  10. final long ident = Binder.clearCallingIdentity();
  11. for (;;) {
  12. Message msg = queue.next(); // might block
  13. if (msg == null) {
  14. // No message indicates that the message queue is quitting.
  15. return;
  16. }
  17. // This must be in a local variable, in case a UI event sets the logger
  18. final Printer logging = me.mLogging;
  19. if (logging != null) {
  20. logging.println(">>>>> Dispatching to " + msg.target + " " +
  21. msg.callback + ": " + msg.what);
  22. }
  23. final long traceTag = me.mTraceTag;
  24. if (traceTag != 0) {
  25. Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
  26. }
  27. try {
  28. msg.target.dispatchMessage(msg);//通过Handler分发消息
  29. } finally {
  30. if (traceTag != 0) {
  31. Trace.traceEnd(traceTag);
  32. }
  33. }
  34. if (logging != null) {
  35. logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
  36. }
  37. // Make sure that during the course of dispatching the
  38. // identity of the thread wasn't corrupted.
  39. final long newIdent = Binder.clearCallingIdentity();
  40. if (ident != newIdent) {
  41. Log.wtf(TAG, "Thread identity changed from 0x"
  42. + Long.toHexString(ident) + " to 0x"
  43. + Long.toHexString(newIdent) + " while dispatching to "
  44. + msg.target.getClass().getName() + " "
  45. + msg.callback + " what=" + msg.what);
  46. }
  47. msg.recycleUnchecked();
  48. }
  49. }
复制代码
  在 loop 中首先获取了当前所在线程的 Looper ,同时也获取到了 Looper 中的 MessageQueue ,说明 Looper 已经与当前的线程进行了绑定。在后面开启了一个 for 的死循环,发现它做的事件是不断的从消息队列中取出消息,最后都交给 msg.target 调用它的 dispatchMessage 方法,那么 target 又是什么呢?我们进入 Message
  Message

  1. /*package*/ int flags;
  2. /*package*/ long when;
  3. /*package*/ Bundle data;
  4. /*package*/ Handler target;
  5. /*package*/ Runnable callback;
  6. // sometimes we store linked lists of these things
  7. /*package*/ Message next;
复制代码
  发现它就是我们熟悉的 Handler ,说明最后调用的就是 Handler 中的 dispatchMessage 方法,对消息的分发处理。这样一来 Handler 就通过 Looper 联系上了 Looper 所绑定的线程,即为主线程。
  Handler

  1. public Handler(Callback callback, boolean async) {
  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. mLooper = Looper.myLooper();
  11. if (mLooper == null) {
  12. throw new RuntimeException(
  13. "Can't create handler inside thread that has not called Looper.prepare()");
  14. }
  15. mQueue = mLooper.mQueue;
  16. mCallback = callback;
  17. mAsynchronous = async;
  18. }
复制代码
  通过 Handler 的初始化,它获取了它所处线程的 Looper ,同时也获取了 Looper 中的消息队列。当然如果所处线程的 Looper 为空的话就会抛出异常,这就解释了为什么在非主线程中创建 Handler 要分别调用 Looper.prepare 与 Looper.loop 而主线程则不需要,因为它默认已经调用了。
  dispatchMessage

  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. }
  13. private static void handleCallback(Message message) {
  14. message.callback.run();
  15. }
复制代码
  回到前面,对于 dispatchMessage 的处理,首先判断 msg.callback 是否为空,这里 callback 通过上面的 Message 应该能知道他就是一个 Runnable ,如果不为空则直接调用 Runnable 的 run 方法。否则调用 Handler 的 handleMessage 方法.而这个方法相信大家已经很熟悉了,对事件的处理都是在这个方法中执行的。因为通过前面我们已经知道了 Handler 已经联系上了主线程,所以 handleMessage 中的处理自然相对于在主线程中进行,自然也能更新 UI 了。通过这里我们能把 Looper 比作是一个桥梁,来连接 Looper 所在的线程与 Handler 之间的通信,同时管理消息队列 MessageQueue 中的消息。那么前面的 Runnable 又是如何不为空的呢?我们使用 Handler 有两种方法,一种是直接创建一个 Handler 并且重写它的 handleMessage 方法,而另一种可以通过 Handler.post(Runnable) 来使用,这样事件的处理自然就在 run 方法中实现。
   上面介绍了 Handler 是如何联系上了需要操作的线程与对消息是如何取出与处理的。下面来谈谈消息是如何放入到 Looper 中的 MessageQueue 中的。
  sendMessageAtTime

   通过 Handler 发送消息的方式很多,例如: sendMessage 、 sendEmptyMessage 与 sendMessageDelayed 等,其实到最后他们调用的都是 sendMessageAtTime 方法。所以还是来看下 sendMessageAtTime 方法中的实现。
  1. public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
  2. MessageQueue queue = mQueue;
  3. if (queue == null) {
  4. RuntimeException e = new RuntimeException(
  5. this + " sendMessageAtTime() called with no mQueue");
  6. Log.w("Looper", e.getMessage(), e);
  7. return false;
  8. }
  9. return enqueueMessage(queue, msg, uptimeMillis);
  10. }
复制代码
  而 sendMessageAtTime 则就是调用了 enqueueMessage 操作,看这方法名就知道是入队列操作了。
  enqueueMessage

  1. private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
  2. msg.target = this;
  3. if (mAsynchronous) {
  4. msg.setAsynchronous(true);
  5. }
  6. return queue.enqueueMessage(msg, uptimeMillis);
  7. }
复制代码
  果不其然直接调用了 MessageQueue 中的 queue.enqueueMessage(msg, uptimeMillis) 将消息加入消息队列,同时这段代码 msg.target = this 将当前的 Handler 赋给了 msg.target ,这就是前面所说的 Looper.loop 方法中调用的 Handler 。这样就把消息放到了 MessageQueue 中,进而通过前面所讲的 loop 来取出消息进行相应的处理,这样就构成了整个对消息进行处理的系统。这也是使用 Handler 内部所发生的原理。好了 Handler 、 Looper 与 MessageQueue 它们之间的联系基本就是这些了。我也简单画了张图希望有所帮助
   

Handler、Looper与MessageQueue源码分析

Handler、Looper与MessageQueue源码分析-1-技术控-Android,defaults,private,public,change

  总结

   来总结下它们之间的流程。首先创建 Handler 而在 Handler 所处的线程中必须要有一个 Looper ,如果在主线程中默认帮我们实现了,其他线程必须调用 Looper.prepare 来创建 Looper 同时调用 Looper.loop 开启对消息的处理。每个 Looper 中都有一个 MessageQueue 它是用来存储 Message 的, Handler 通过 post 或者 send.. 等一系列操作通过 Looper 将消息放入到消息队列中,而 Looper 通过开启一个无限的循环来一直监听着消息的处理,不断从 MessageQueue 中取出消息,并交给与当前 Looper 所绑定的 handler 的 dispatchMessage 进行分发,最后根据情况调用 Runnable 的 run 或者 Handler 的 HandlerMessage 方法对消息进行最后的处理。
e9wy2s9dfi 发表于 2016-10-19 18:01:54
风萧萧兮易水寒,欠了钱兮你要还!
回复 支持 反对

使用道具 举报

野蛮的年轻人 发表于 2016-10-28 10:51:00
我只是路过,不发表意见
回复 支持 反对

使用道具 举报

董佳昕 发表于 2016-11-7 10:57:03
我消极对待减肥,能不能取消我胖子的资格啊
回复 支持 反对

使用道具 举报

如烟绕梁 发表于 2016-11-10 09:23:14
大家好!我是如烟绕梁
回复 支持 反对

使用道具 举报

刘光英 发表于 2016-11-21 19:39:45
放假前的节奏
回复 支持 反对

使用道具 举报

我要投稿

回页顶回复上一篇下一篇回列表
手机版/c.CoLaBug.com ( 粤ICP备05003221号 | 文网文[2010]257号 | 粤公网安备 44010402000842号 )

© 2001-2017 Comsenz Inc.

返回顶部 返回列表