Handler消息机制源码解析(三)

来源:互联网 发布:ubuntu live桌面用户名 编辑:程序博客网 时间:2024/04/29 07:03

这一篇博客主要为大家介绍Handler怎么接收到消息的,大家都知道Handler接收消息是在handleMessage方法里面,但是具体怎么接收到的很多人并不知道,下面让我们一起去看一下。大家都知道在子线程中实例化Handler对象之前除了了要添加Loop.prepare(),在之后还需要写Loop.loop()。在前两篇文章都没有提到Loop.loop()这个方法,第三篇就要提到这个方法了,因为接收消息的奥秘就在这个方法里面。大家请看Loop.loop()方法的源代码。

    public static void loop() {        final Looper me = myLooper();        if (me == null) {            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");        }        final MessageQueue queue = me.mQueue;        // Make sure the identity of this thread is that of the local process,        // and keep track of what that identity token actually is.        Binder.clearCallingIdentity();        final long ident = Binder.clearCallingIdentity();        for (;;) {            Message msg = queue.next(); // might block            if (msg == null) {                // No message indicates that the message queue is quitting.                return;            }            // This must be in a local variable, in case a UI event sets the logger            Printer logging = me.mLogging;            if (logging != null) {                logging.println(">>>>> Dispatching to " + msg.target + " " +                        msg.callback + ": " + msg.what);            }            msg.target.dispatchMessage(msg);            if (logging != null) {                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);            }            // Make sure that during the course of dispatching the            // identity of the thread wasn't corrupted.            final long newIdent = Binder.clearCallingIdentity();            if (ident != newIdent) {                Log.wtf(TAG, "Thread identity changed from 0x"                        + Long.toHexString(ident) + " to 0x"                        + Long.toHexString(newIdent) + " while dispatching to "                        + msg.target.getClass().getName() + " "                        + msg.callback + " what=" + msg.what);            }            msg.recycleUnchecked();        }    }

大家请看第三行代码final Looper me = myLoop();那么myLoop()这个方法里面又是什么呢?

 public static Looper myLooper() {        return sThreadLocal.get();    }

OK,这下子一切都清楚了myLooper()方法获得的其实就是我们前面通过Loop.prepare()给各自不同的线程的Loop,存储在ThreadLocal(前面已经介绍过了)里面,大家请看第八行代码,第八行代码其实就是前面所说的每个Loop有各自对应的MessageQueue,把各自的消息队列找出来,大家从17行开始看,发现这是一个无限循环,如果消息为空,就会一直重复循环不走下去,如果消息不为空就会调用msg.target.dispatchMessage(msg)方法,前面已经特别强调过msg.target就是相对应的handler,那么我们再来看一下这个方法里面的源码又是什么呢?

public void dispatchMessage(Message msg) {        if (msg.callback != null) {            handleCallback(msg);        } else {            if (mCallback != null) {                if (mCallback.handleMessage(msg)) {                    return;                }            }            handleMessage(msg);        }    }

大家请看这边分别有三个判断哦,第一个是msg.callback,第二个事mCallback,如果这两个都为空才轮到handleMessage(msg)的方法。
这边我就来和大家一起探讨一下这三个分别是什么鬼?第一说到msg.callback,大家还记得上一篇为大家看过的Handler.post(Runnable)方法么?让我们再次来看一下那段源码

private static Message getPostMessage(Runnable r) {        Message m = Message.obtain();        m.callback = r;        return m;    }

OK,一切都清楚,其实msg.callback就是Runnable接口,那最后又是怎么运行的呢?

 private static void handleCallback(Message message) {        message.callback.run();    }

相信看到这里,各位有一种上当受骗的没错,它只是把Runnable接口简单的run了一下而已哈哈,这也是为啥可以在里面直接更新UI,因为他本来就在主线程里面。
那么下面来为各位讲述第二个mCallBack,这个时候大家是不是突然想起我们在Handler里面的构造方法里面想起曾经还有一个CallBack,没错就是那个,如果有那个会怎么样呢?

  public interface Callback {        public boolean handleMessage(Message msg);    }

依然是调用handleMessage()方法,这里就有人要问了为啥要设置这个Callback,有啥意义,其实我也没明白有啥意义,希望大神解答一下小弟的困惑。其实讲了半天,有个很重要的概念没有提出,就是Handler到底靠什么在线程之间进行切换的,下面就给大家把这个概念理一理。每个线程有一个Looper,每一个Looper有一个对应的消息队列, 每一个消息都有一个msg.target其实就是发送这个消息的Handler.OK,这下其实就清楚了,如果在不同的线程里面,因为Looper不同,所以消息并不在相同的消息队列里面,所以不同线程切换的唯一通道就是Handler的实例化对象。只要是这个Handler发的这个消息,这个Handler的handMessage方法就一定能接收到这个。那么相同线程呢,不同界面呢,因为如果是相同的线程Looper是一样的,所以无论在哪个界面Looper都能找到消息,所以在相同的线程即使在不同的界面,即使不是同一个Handler也同样能够接收到消息。OK,这下基本将Handler如何接收到消息讲述完了。大家肯定还有一个困惑,就是在主线程当中为啥不需要Loop.loop(),其实这个和Loop.prepare()方法是一样的,大家请再次看一下这段代码。

public static void main(String[] args) {          SamplingProfilerIntegration.start();          CloseGuard.setEnabled(false);          Environment.initForCurrentUser();          EventLogger.setReporter(new EventLoggingReporter());          Process.setArgV0("<pre-initialized>");          Looper.prepareMainLooper();          ActivityThread thread = new ActivityThread();          thread.attach(false);          if (sMainThreadHandler == null) {              sMainThreadHandler = thread.getHandler();          }          AsyncTask.init();          if (false) {              Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread"));          }          Looper.loop();          throw new RuntimeException("Main thread loop unexpectedly exited");      }  

因为在主线程早就调用过了,所以无需再次调用。Handler消息机制源码解析基本告一段落,最后一篇Handler消息机制源码解析(四)将为大家介绍Handler使用的一些小优化技巧以及会出现的内存泄露的情况和解决这种内存泄露的办法。

0 0
原创粉丝点击