Handler通信机制源码解读

本文深入解析Android的Handler、Looper、MessageQueue和Message的源码,阐述它们如何构成Android的消息机制。文章指出,了解内部原理有助于解决复杂问题。Handler通过sendMessage将消息放入MessageQueue,Looper不断从队列取出消息,回调Handler的handleMessage方法,实现线程通信。文章还探讨了UI线程与子线程创建Handler的区别,并分析了Looper无限循环不会导致ANR的原因。
摘要由CSDN通过智能技术生成

工作中,我们可能直接使用我们需要的工具方法,但是不曾了解其中的原理内涵,这样并不能很好的让我们理解其运行机制,在复杂项目和疑难问题时无从入手。作为开发想要提高并设计架构,一是要先学习优秀的设计理念,再就是了解其内部原理,为自己在复杂使用场景和发生疑难问题时能够透过表象看到本质。
Handler,Message,looper 和 MessageQueue 构成了安卓的消息机制,handler创建后可以通过 sendMessage 将消息加入消息队列,然后 looper不断的将消息从 MessageQueue 中取出来,回调到 Hander 的 handleMessage方法,从而实现线程的通信。
从两种情况来说,第一在UI线程创建Handler,此时我们不需要手动开启looper,因为在应用启动时,在ActivityThread的main方法中就创建了一个当前主线程的looper,并开启了消息队列,消息队列是一个无限循环,为什么无限循环不会ANR?因为可以说,应用的整个生命周期就是运行在这个消息循环中的,安卓是由事件驱动的,Looper.loop不断的接收处理事件,每一个点击触摸或者Activity每一个生命周期都是在Looper.loop的控制之下的,looper.loop一旦结束,应用程序的生命周期也就结束了。我们可以想想什么情况下会发生ANR,第一,事件没有得到处理,第二,事件正在处理,但是没有及时完成,而对事件进行处理的就是looper,所以只能说事件的处理如果阻塞会导致ANR,而不能说looper的无限循环会ANR
另一种情况就是在子线程创建Handler,此时由于这个线程中没有默认开启的消息队列,所以我们需要手动调用looper.prepare(),并通过looper.loop开启消息
主线程Looper从消息队列读取消息,当读完所有消息时,主线程阻塞。子线程往消息队列发送消息,并且往管道文件写数据,主线程即被唤醒,从管道文件读取数据,主线程被唤醒只是为了读取消息,当消息读取完毕,再次睡眠。因此loop的循环并不会对CPU性能有过多的消耗。

Handler源码部分解读:

初级程序员使用handler时有时会出现异常。Can’t create handler inside thread that has not called Looper.prepare()
为什么呢。
在core/java/android/os/Handler.java 中

 @hide
     */
    public Handler(@Nullable Callback callback, boolean async) {
   
        if (FIND_POTENTIAL_LEAKS) {
   
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
   
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        mLooper = Looper.myLooper();
        if (mLooper == null) {
   
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

在其初始化的结构体中,获取mLooper = Looper.myLooper();通过检测mLooper是否为空,为空则报上述异常。如果不为空, 再通过mQueue = mLooper.mQueue;获取消息队列,可以看到messageQuene对象是在Looper中获取的。
再往下看,Handler中提供获取主线程Handler的方法。

  /** @hide */
    @UnsupportedAppUsage
    @NonNull
    public static Handler getMain() {
   
        if (MAIN_THREAD_HANDLER == null) {
   
            MAIN_THREAD_HANDLER = new Handler(Looper.getMainLooper());
        }
        return MAIN_THREAD_HANDLER;
    }

之后,我们我们发现了我们常用的obtainMessage方法。为何建议使用obtainMessage而不是new Message()呢,从obtainMessage的注释中也可以看出来,其会从一个全局message pool中返回一个新的message,比新建一个实例更有效率。另外的比如obtainMessage(int what)和obtainMessage(int what, @Nullable Object obj)则同步设置了message.what和obj消息。

/**
     * Returns a new {@link android.os.Message Message} from the global message pool. More efficient than
     * creating and allocating new instances. The retrieved message has its handler set to this instance (Message.target == this).
     *  If you don't want that facility, just call Message.obtain() instead.
     */
    @NonNull
    public final Message obtainMessage()
    {
   
        return Message.obtain(this);
    }

然后是各种发送消息的方法,包括发送message消息的和post(runnable)的方法。可以看到无论是send还是post还是带时间参数的,最终都走到了queue.enqueueMessage。这个messagequeue在下边的小节会解读。
Handler中消息发送方法调用

Looper源码解读:

在Handler源码中我们看到其Looper是用Looper.myLooper获取的,这里looper相当于消息的传送带,不断的循环检测是否有消息发过来。
在Looper类文件的开头我们就看到了谷歌给我们的注释和一个例子,清楚的说明了普通现场默认是没有looper的,需要手动创建,然后创建的方法就是Looper.prepare(),然后下面的例子也清楚的解释了上节中报错信息如何防止,Can’t create handler inside thread that has not called Looper.prepare()。然后启动looper循环用Looper.loop(),以启动消息循环,直到loop被停止。

/**
  * Class used to run a message loop for a thread.  Threads by default do
  * not have a message loop associated with them; to create one, call
  * {@link #prepare} in the thread that is to run the loop, and then
  * {@link #loop} to have it process messages until the loop is stopped.
  *
  * <p>Most interaction with a message loop is through the
  * {@link Handler} class.
  *
  * <p>This is a typical example of the implementation of a Looper thread,
  * using the separation of {@link #prepare} and {@link #loop} to create an
  * initial Handler to communicate with the Looper.
  *
  * <pre>
  *  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();
  *      }
  *  }</pre>
  */

然后prepare的方法定义,可以看到sThreadLocal.get() != null的判断,这里可以看出,一个线程只能创建一个looper,但是如何保证的呢,代码如何实现一个线程一个looper的呢。这里就需要看代码中的sThreadLocal的get和set方法了。

/** Initialize the current thread as a looper.
      * This gives you a chance to create handlers that then reference
      * this looper, before actually starting the 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值