Android Handler机制4之Looper与Handler简介

Android Handler机制系列文章整体内容如下:

本片文章的主要内容如下:

  • 1 概述
  • 2 相应类的理解
  • 3 Handle原理详解
  • 4 主线程的Looper的初始化

一、 概述

Android Handler官网

工欲善其事必先利其器,在Android的源码世界里面,有两大利器,一是Binder IPC机制,关于Binder的内容上面已经讲过了。另一个便是Handler消息机制(由Handler/Looper/MessageQueue等构成)。Android有大量的消息驱动方法来进行交互,就像Android的四大组件(Activity、Service、Broadcast、ContentProvider)的启动过程交互,都离不开Handler的消息机制,所以Android系统某种意义上说也是一种以消息驱动的系统。

那Android为什么要采用Handler的消息机制那?

答: 在Android中,只有主线程才能更新UI,但是主线程不能进行耗时操作,否则会产生ANR异常,所以常常把耗时操作放到其他子线程进程。如果在子线程中需要更新UI,一般都是通过Handler发送消息,主线接受消息并进行相应的逻辑处理。当然除了直接使用Handler,还可以用View的post()方法、Activity的runOnUIThread()方法来更新UI和AsyncTask其实他们本质也是使用了Handler来实现的。后面我们会单独说他们

要理解Handler的消息机制,就不得不说Handler/Looper/Message/MessageQueue/Message这四4个类,下面我们先大概了解下这几个类

二、 相应类的理解

关于类的理解,还是以官网上的类介绍为主

(一) Handler

Handler

1、什么是Handler

A Handler allows you to send and process Message
and Runnable objects associated with a thread's MessageQueue
. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.

简单翻一下:

Handler是一个可以通过关联一个消息队列来发送和处理消息,发送或处理Runnable对象的一个处理程序,每个Handler都关联一个单个的线程和消息队列,当你创建一个新的Handler的时候它就将绑定到一个线程或线程上的消息队列,从那时起,这个Handler就将为这个消息队列提供消息或Runnable对象,处理消息队列释放出来的消息或Runnable对象。

2、Handler有什么用

There are two main uses for a Handler: (1) to schedule messages and runnables to be executed as some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.

简单翻译 一下:

Handler有两个主要的用途

  • 1 安排消息和Runnable对象在未来之星
  • 2 将你的一个动作放在不同的线程上执行
3、怎么使用Handler

Scheduling messages is accomplished with the post(Runnable), postAtTime(Runnable, long), postDelayed(Runnable, long), sendEmptyMessage(int), sendMessage(Message), sendMessageAtTime(Message, long), and sendMessageDelayed(Message, long) methods. The post versions allow you to enqueue Runnable objects to be called by the message queue when they are received; the sendMessage versions allow you to enqueue a Message object containing a bundle of data that will be processed by the Handler's handleMessage(Message) method (requiring that you implement a subclass of Handler).

翻译一下:

通过重写post(Runnable)postAtTime(Runnable, long)postDelayed(Runnable, long)sendEmptyMessage(int)sendMessage(Message)sendMessageAtTime(Message, long)sendMessageDelayed(Message, long)来完成发送消息。当前版本允许你通过消息队列接受一个Runnable对象,sendMessage方法当前版本允许你将一个包的数据通过消息队列的方式处理,但是你需要重写Handler的handleMessage方法

When posting or sending to a Handler, you can either allow the item to be processed as soon as the message queue is ready to do so, or specify a delay before it gets processed or absolute time for it to be processed. The latter two allow you to implement timeouts, ticks, and other timing-based behavior.

翻译一下:

当你发送一个Handler时,你可以使消息队列(MessageQueue)尽快去处理已经准备好的条目,或者指定一个延迟处理的时间或者指定时间处理,后两者允许你实现超时,Ticks(系统相对的时间单位)和其他时间段为基础的行为。

When a process is created for your application, its main thread is dedicated to running a message queue that takes care of managing the top-level application objects (activities, broadcast receivers, etc) and any windows they create. You can create your own threads, and communicate back with the main application thread through a Handler. This is done by calling the same post or sendMessage methods as before, but from your new thread. The given Runnable or Message will then be scheduled in the Handler's message queue and processed when appropriate.

即当一个进程被应用程序创建时,它的主线程会运行一个消息队列负责管理它创建的高层应用程序对象(如Activity、Broadcast Receiver等)和任何它的窗口创建的对象,你可以通过一个Handler,创建自己的线程来实现与主线程之间的交互,但前提是你得在你的线程重写sendMessage方法并写上一行行代码,这样你给定的Runnable或者Message将被MessageQueue(消息队列)预定,并在合适的时间处理

注意:有心的同学会注意,我们在进行Handler源码分析的时候,可以知道Handler中的Message是分2中类型的,一种是Message,就是Message对象,一种是CallMessage,就是Runnable对象,但是MessageQueue中只支持DataMessage,再插入到MessageQueue的时候,会把Runnable对象封装到Message对象中。

(二) Looper

Looper

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 prepare() in the thread that is to run the loop, and then loop() to have it process messages until the loop is stopped.
Most interaction with a message loop is through the Handler class.
This is a typical example of the implementation of a Looper thread, using the separation of prepare() and loop() to create an initial Handler to communicate with the Looper.

  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();
      }
  }

翻译一下:

用于为线程运行消息循环的类。默认线程没有与它们相关联的消息喜欢;所以要在运行循环的线程中调用prepare(),然后调用loop()让它循环处理消息,直到循环停止。
与消息循环的交互是通过Handler类
下面这个是一个典型的Looper线程实例,它使用prepar()和loop()的分割来创建一个初始的Handler与Looper进行通信。

三、 Handle原理详解

(一)、模型

Handler的消息机制主要包含:

  • Message:消息
  • MessageQueue:消息队列的
  • Handler:消息管理类向消息池发送各种消息事件
  • Looper:不断的循环执行(Looper.loop),按分发机制将消息分发给目标处理者

(二)、架构图

Handle机制架构图.png

(三)、举例说明

我们先用一个典型的关于Handler/Looper的线程为例展示
代码如下:

class LooperThread extends Thread {
    public Handler mHandler;

    public void run() {
        //情景1
        Looper.prepare();   
        //情景3
        mHandler = new Handler() {  
            public void handleMessage(Message msg) {
                //TODO    定义消息处理逻辑. 
                //情景4
                Message msg=Message.obtain();
            }
        };
       // 情景2
        Looper.loop();  
    }
}

OK,那我们便依照上面的的四个情景依次展开

1、情景1 Looper.prepare();

代码在Looper.java的82行


     /** Initialize the current thread as a looper.
      * This gives you a chance to create handlers that then reference
      * this looper, before actually starting the loop. Be sure to call
      * {@link #loop()} after calling this method, and end it by calling
      * {@link #quit()}.
      */
    public static void prepare() {
        prepare(true);
    }

看下prepare()方法的注释,翻译一下:

初始化当前线程和Looper,这样可以在实际开始启动循环(loop())之前创建一个Handler并且关联一个looper。确保在先调用这个方法,然后调用loop()方法,并且通过调用quit()结束。

我们发现prepare()方法里面什么都没有调用,就是调用了prepare(true),那我们继续跟踪下

1.1、 prepare(boolean)方法

这里面的入参boolean表示Looper是否允许退出,true就表示允许退出,对于false则表示Looper不允许退出。
代码在Looper.java的86行

    private static void prepare(boolean quitAllowed) {
         //每个线程只允许执行一次该方法,第二次执行的线程的TLS已有数据,则会抛出异常。
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //创建Looper对象,并且保存到当前线程的TLS区域。
        sThreadLocal.set(new Looper(quitAllowed));
    }

那sThreadLocal是什么?我们来看下

代码在Looper.java的86行

    // sThreadLocal.get() will return null unless you've called prepare().
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

原来是ThreadLocal啊,关于ThreadLocal的内容请参考Android Handler机制2之ThreadLocal

我们看到上面调用了new Looper(quitAllowed),那我们就来看下这个Looper的构造函

1.2、 Looper(boolean)构造函数
   private Looper(boolean quitAllowed) {
        // 创建MessageQueue对象
        mQueue = new MessageQueue(quitAllowed);
        // 记录当前线程
        mThread = Thread.currentThread();
    }

通过上面代码,我们发现就是创建了一个MessageQueue,并且把当前线程赋值给本地变量的mThread。

  • 1 这样就实现了Looper和MessageQueue的关联
  • 2 这样就实现了Thread和Looper的关联
1.3、 Looper类的思考

因为是构造函数,我们看下Looper这个类的结构,如下图

Looper类结构.png

咦,大家发现没,Loop类就一个构造函数,也就是我们上面的说的Looper(boolean) 这个构造函数。而且这个构造函数也是private。我们又发现在这个私有的Looper(boolean)的构造函数,只有在private static void prepare(boolean quitAllowed)调用,而这个静态方法也是private的,我们看下这个方法在哪里被调用

prepare(boolean)调用处.png

所以我们得出结论

私有的构造函数 => 私有的静态方法prepare(boolean) => static prepare()/prepareMainLooper()

所以我们知道,Looper这个类的对象不能直接创建,必须通过Looper来的两个静态方法prepare()/prepareMainLooper()来间接创建

1.4、prepareMainLooper()方法

代码在Looper.java的99行

    /**
     * Initialize the current thread as a looper, marking it as an
     * application's main looper. The main looper for your application
     * is created by the Android environment, so you should never need
     * to call this function yourself.  See also: {@link #prepare()}
     */
    public static void prepareMainLooper() {
         // 设置不允许退出的Looper
        prepare(false);
        synchronized (Looper.class) {
            //将当前的Looper保存为Looper。每个线程只允许执行一次
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
① 注释

首先翻译一下注释

初始化当前当前线程的looper。并且标记为一个程序的主Looper。由Android环境来创建应用程序的主Looper。因此这个方法不能由咱们来调用。另请参阅prepare()

② 方法解析
  • 首先 通过方法我们看到调用了prepare(false);注意这里的入参是false
  • 其次 做了sMainLooper的非空判断,如果是有值的,直接抛异常,因为这个sMainLooper必须是空,因为主线程有且只能调用一次prepareMainLooper(),如果sMainLooper有值,怎说说明prepareMainLooper()已经被调用了,而sMainLooper的赋值是由myLooper来执行,
  • 最后调用myLooper()方法来给sMainLooper进行赋值。
③ sMainLooper

上面提到了一个变量时sMainLooper,那sMainLooper到底是什么?我们来看下源代码.
代码在Looper.java的 69行

  private static Looper sMainLooper;  // guarded by Looper.class

通道源码我们知道原来就是Looper对象啊。但是它是静态的。而在Java7之前,静态变量存在永久代(PermGen)。在Hospot JVM上,PermGen 就是方法区;在Java7之后,将变量的存储转移到了堆。所以说这个sMainLooper就是主线程的Looper。所以只有通过prepareMainLooper()就可以给主线程Looper赋值了。

1.5、myLooper()方法

代码在Looper.java的173行

    /**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     */
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

这里的sThreadLocal.get()是和prepare(boolean)方法里面的sThreadLocal.set(new Looper(quitAllowed));一一对应的。而在prepareMainLooper()方法里面,

提出一个问题,在prepareMainLooper里面调用myLooper(),那么myLooper()方法的返回有没有可能为null?

答:第一步就是调用prepare(false);,所以说myLooper()这个方法的返回值是一定有值的。

2、情景2 Looper.loop();

代码在Looper.java的 122行

    /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    public static void loop() {
         // 获取TLS存储的Looper对象
        final Looper me = myLooper();
        //没有Looper 对象,直接抛异常
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        //获取当前Looper对应的消息队列
        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();
        // 进入 loop的主循环方法
       // 一个死循环,不停的处理消息队列中的消息,消息的获取是通过MessageQueue的next()方法实现
        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
            // 默认为null,可通过setMessageLogging()方法来指定输出,用于debug功能
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }
           // 用于分发消息,调用Message的target变量(也就是Handler了)的dispatchMessage方法来处理消息
            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.
            // 确保分发过程中identity不会损坏
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                // 打印identiy改变的log,在分发消息过程中是不希望身份被改变
                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);
            }
            // 将Message放入消息池
            msg.recycleUnchecked();
        }
    }

loop进入循环模式,不断重复下面的操作,直到没有消息时退出循环

  • 读取MessageQueue的下一条Message
  • 把Message分发给相应的target
  • 再把分发后的Message回到消息池,以便重复利用
这里有几个重要方法,我们分别会在后面的文章中说明
3 Looper的退出循环方法

上面了开启循环的方法,那么怎么退出循环?Looper里面退出循环有两个方法分别是quit()quitSafely()方法

3.1、Looper.quit()方法

代码在Looper.java的 227行

    /**
     * Quits the looper.
     * <p>
     * Causes the {@link #loop} method to terminate without processing any
     * more messages in the message queue.
     * </p><p>
     * Any attempt to post messages to the queue after the looper is asked to quit will fail.
     * For example, the {@link Handler#sendMessage(Message)} method will return false.
     * </p><p class="note">
     * Using this method may be unsafe because some messages may not be delivered
     * before the looper terminates.  Consider using {@link #quitSafely} instead to ensure
     * that all pending work is completed in an orderly manner.
     * </p>
     *
     * @see #quitSafely
     */
    public void quit() {
        mQueue.quit(false);
    }
① 注释

简单的翻译一下:

  • 退出循环
  • 将终止(loop()方法)而不处理消息队列中的任何更多消息。在调用quit()后,任何尝试去发送消息都是失败的。例如Handler.sendMessage(Message)方法将返回false。因为循环终止之后一些message可能会被无法传递,所以这个方法是不安全的。可以考虑使用quitSafely()方法来确保所有的工作有序地完成。
② 方法内容:

Looper的quit()方法内部的本质是调用mQueue的quit()的方法,入参是false

3.2、Looper.quitSafely()方法

代码在Looper.java的 227行

    /**
     * Quits the looper safely.
     * <p>
     * Causes the {@link #loop} method to terminate as soon as all remaining messages
     * in the message queue that are already due to be delivered have been handled.
     * However pending delayed messages with due times in the future will not be
     * delivered before the loop terminates.
     * </p><p>
     * Any attempt to post messages to the queue after the looper is asked to quit will fail.
     * For example, the {@link Handler#sendMessage(Message)} method will return false.
     * </p>
     */
    public void quitSafely() {
        mQueue.quit(true);
    }
① 注释

简单的翻译一下:

  • 安全退出循环
  • 调用quitSafely()方法会使循环结束,只要消息队列中已经被传递的所有消息都将被处理。然而,在循环结束之前,将来不会提交处理延迟消息。
  • 调用退出后,所有尝试去发送消息都将失败。就像调用Handler.sendMessage(Message)将返回false。
② 方法内容:

Looper的quit()方法内部的本质是调用mQueue的quit()的方法,入参是true

3.3、MessageQueue.quit(boolean)方法

代码在MessageQueue.java的 413行

    void quit(boolean safe) {
        //当mQuitAllowed为false,表示不运行退出,强行调用quit()会超出异常
        //mQuitAllowed 是在Looper构造函数里面构造MessageQueue()以参数参进去的
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }

        synchronized (this) {
            // 防止多次执行退出操作
            if (mQuitting) {
                return;
            }
            mQuitting = true;

            if (safe) {
                //移除尚未触发的所有消息
                removeAllFutureMessagesLocked();
            } else {
                //移除所有消息
                removeAllMessagesLocked();
            }

            // We can assume mPtr != 0 because mQuitting was previously false.
             //mQuitting=false,那么认定mPtr!=0
            nativeWake(mPtr);
        }
    }

消息退出的方式:

  • 当safe=true,只移除尚未触发的所有消息,对于正在触发的消息并不移除
  • 当safe=false,移除所有消息
4、情景3:创建Handler对象

看到Handler对象,首先要看下Handler的构造函数


Handler的构造函数.png

好多的构造函数啊,一共7个,其中6个有参的构造函数,1个有参的构造函数,所有的构造函数都是public的,为了大家更好的理解,我将这些构造函数编号如下:

  • ① public Handler()
  • ② public Handler(Callback callback)
  • ③ public Handler(Looper looper)
  • ④ public Handler(Looper looper, Callback callback)
  • ⑤ public Handler(boolean async)
  • ⑥ public Handler(Callback callback, boolean async)
  • ⑦ public Handler(Looper looper, Callback callback, boolean async)

上面的7个Handler的构造函数大体上可以分为两大类:

  • 两个参数的Handler构造函数
  • 三个参数的Handler构造函数
4.1 两个参数的Handler构造函数

这里说一下 ① ② ⑤ ⑥ 这四个是"两个参数的Handler构造函数",为什么这么说,大家可以看下源码

4.1.1构造函数①,代码在Handler.java的 113行
    /**
     * Default constructor associates this handler with the {@link Looper} for the
     * current thread.
     *
     * If this thread does not have a looper, this handler won't be able to receive messages
     * so an exception is thrown.
     */
    public Handler() {
        this(null, false);
    }

他内部其实是调用的构造函数⑥,只不过第一个入参为null,第二个参数为false

4.1.2 构造函数②,代码在Handler.java的 127行
    /**
     * Constructor associates this handler with the {@link Looper} for the
     * current thread and takes a callback interface in which you can handle
     * messages.
     *
     * If this thread does not have a looper, this handler won't be able to receive messages
     * so an exception is thrown.
     *
     * @param callback The callback interface in which to handle messages, or null.
     */
    public Handler(Callback callback) {
        this(callback, false);
    }

他内部其实是调用的构造函数⑥,只不过第一个入参为callback,第二个参数为false

4.1.3 构造函数⑤,代码在Handler.java的 167行
    /**
     * Use the {@link Looper} for the current thread
     * and set whether the handler should be asynchronous.
     *
     * Handlers are synchronous by default unless this constructor is used to make
     * one that is strictly asynchronous.
     *
     * Asynchronous messages represent interrupts or events that do not require global ordering
     * with respect to synchronous messages.  Asynchronous messages are not subject to
     * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
     *
     * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
     * each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
     *
     * @hide
     */
    public Handler(boolean async) {
        this(null, async);
    }

他内部其实是调用的构造函数⑥,只不过第一个入参为null,第二个参数为async

综上所述我们知道 构造函数① ② ⑤ ,归根结底都是调用的是构造函数⑥,那我们就来看下构造函数⑥

4.1.4 构造函数⑥,代码在Handler.java的 188行
    /**
     * Use the {@link Looper} for the current thread with the specified callback interface
     * and set whether the handler should be asynchronous.
     *
     * Handlers are synchronous by default unless this constructor is used to make
     * one that is strictly asynchronous.
     *
     * Asynchronous messages represent interrupts or events that do not require global ordering
     * with respect to synchronous messages.  Asynchronous messages are not subject to
     * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
     *
     * @param callback The callback interface in which to handle messages, or null.
     * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
     * each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
     *
     * @hide
     */
    public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            //如果是匿名类、内部类、局不类(方法内部),且没有声明为STATIC,则存在内存泄露风险,所以要打印日志提醒开发者
            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());
            }
        }
        //获取当前线程的Looper。
        mLooper = Looper.myLooper();
        //如果当前线程没有Looper,则说明没有调用Looper.prepare(),抛异常
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        //把Looper的Queue赋值给Handler的mQueue
        mQueue = mLooper.mQueue;
         // mCallback 赋值
        mCallback = callback;
         // mAsynchronous 赋值
        mAsynchronous = async;
    }

老规矩,先来看下注释,简单翻译如下:

  • 使用当前线程的Looper,使用这个指定的Callback接口,并且设置这个handler是否是异步的。
  • 除非在构造函数设置为异步的,否则默认的的Handler是同步的。
  • 异步消息相对于同步消息的而言的,表示消息不会受到中断或者事件的影响其全局顺序。异步消息是不受到MessageQueue.enqueueSyncBarrier(long)的同步障碍影响。

通过上面代码我们我们知道

  • Handler默认采用的是当前线程TLS中的Looper对象,只要执行了Looper.prepare()方法,就可以可以获取有效的Looper对象,同时我们上面说了Looper的构造函数关联了MessageQueue和Thread。而在Handler的构造函数里面Handler里面的MessageQueue即是Looper的MessageQueue。
    所以在同一个线程里面(已经调用了Looper.prepare()),Handler里面的MessageQueue和的Looper的MessageQueue指向了同一个对象

所以我们也说这这个方法让Handler对象和Looper对象绑定在一起了。

上面提到了Callback,是个什么?让我们来看下
代码在Handler.java的 80行

    /**
     * Callback interface you can use when instantiating a Handler to avoid
     * having to implement your own subclass of Handler.
     *
     * @param msg A {@link android.os.Message Message} object
     * @return True if no further handling is desired
     */
    public interface Callback {
        public boolean handleMessage(Message msg);
    }

简单翻译一下:

当你实例化Handler的时候可以使用Callback接口,这样可以避免你自己去去实现Handler的子类

有点复杂,大家怎么理解?大家平时写Handler不是一般是写一个匿名内部类居多吧,如下:

    private Handler handler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    };

如果存在一个回调的需求,就可以直接使用Callback了,就不用去实现Handler的子类了。

4.2 三个参数的Handler构造函数

这里说一下③、④、⑦ 这三个是"三个参数的Handler构造函数",为什么这么说,依旧看下源码

4.2.1构造函数③,代码在Handler.java的 136行
    /**
     * Use the provided {@link Looper} instead of the default one.
     *
     * @param looper The looper, must not be null.
     */
    public Handler(Looper looper) {
        this(looper, null, false);
    }

他内部其实是调用的构造函数⑦,只不过第一个入参为looper,第二个参数为null,第三个参数为false。

4.2.2构造函数④,代码在Handler.java的 147行
    /**
     * Use the provided {@link Looper} instead of the default one and take a callback
     * interface in which to handle messages.
     *
     * @param looper The looper, must not be null.
     * @param callback The callback interface in which to handle messages, or null.
     */
    public Handler(Looper looper, Callback callback) {
        this(looper, callback, false);
    }

他内部其实是调用的构造函数⑦,只不过第一个入参为looper,第二个参数为callback,第三个参数为false。

4.2.3构造函数⑦,代码在Handler.java的 227行
    /**
     * Use the provided {@link Looper} instead of the default one and take a callback
     * interface in which to handle messages.  Also set whether the handler
     * should be asynchronous.
     *
     * Handlers are synchronous by default unless this constructor is used to make
     * one that is strictly asynchronous.
     *
     * Asynchronous messages represent interrupts or events that do not require global ordering
     * with respect to synchronous messages.  Asynchronous messages are not subject to
     * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
     *
     * @param looper The looper, must not be null.
     * @param callback The callback interface in which to handle messages, or null.
     * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
     * each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
     *
     * @hide
     */
    public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

老规矩,先来看下注释,简单翻译如下:

  • 不使用默认的Looper,使用由第一个入参提供的Looper,并采取回调接口Callback来处理消息,同时也可以设置是否是异步的。
  • Handler默认是同步的,如果在构造函数里面设置了异步,才会变成异步的。
  • 异步消息相对于同步消息的而言的,表示消息不会受到中断或者事件的影响其全局顺序。异步消息是不受到MessageQueue.enqueueSyncBarrier(long)的同步障碍影响。

通过上面代码我们我们知道

  • 首先根据给定的Looper赋值本地变量mLooper和mQueue。这样实现了Handler和Looper的关联
4.3 Handler构造函数总结

综合7个构造函数我们发现其实构造函数的本质都是给下面四个本地变量赋值mLooper、mQueue、mCallback、mAsynchronous。那我们就一次分析下:

  • 1、如果在构造函数中没有给出Looper,则使用默认的Looper,通过Looper.mQueue来给mQueue赋值,实现Handler、Looper、MessageQueue三者绑定。
    -通过构造函数来设置Callback的回调接口,不设置则为null
    -通告mAsynchronous设控制是同步的还是异步的,而mAsynchronous的值默认是false的,这个mAsynchronous可以通过构造函数来设置。

总结如下:

Handler构造总结.png

有同学说图片看不清,按我就弄个表格

Hander本地变量 ① Handler() ② Handler(Callback) ③ Handler(Looper) ④ Handler(Looper, Callback) ⑤ Handler(boolean) ⑥ Handler(Callback, boolean) ⑦ Handler(Looper,Callback, boolean)
mLooper(Looper对象) Looper.myLooper() Looper.myLooper() looper looper Looper.myLooper() Looper.myLooper() looper
mQueue(MessageQueue对象) null callback null callback null callback callback
mAsynchronous(boolean类型) false false false false async async async

四、 主线程的Looper的初始化

上面说了很多,那么主线程的Looper是什么时候初始化的那?是在系统启动的时候,初始化的。

代码在ActivityThread.java 5401行

    public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        SamplingProfilerIntegration.start();

        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);

        Environment.initForCurrentUser();

        // Set the reporter for event logging in libcore
        EventLogger.setReporter(new EventLoggingReporter());

        AndroidKeyStoreProvider.install();

        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);

        Process.setArgV0("<pre-initialized>");

        /*** 重点 */
        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }


        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        /*** 重点 */
        Looper.loop();
       throw new RuntimeException("Main thread loop unexpectedly exited");
    }

PS:这里补充一个概念ActivityThread不是一个thread,ActivityThread是一个类,大家仔细看他的源代码ActivityThread并没有实现Thread,大家千万不要跑偏了。

通过上面的代码,我们发现

  • 首先调用了Looper的静态方法prepareMainLooper()给主线程绑定一个Looper,同时设置Looper对应的MessageQueue对象的mQuitAllowed为false,则该messageQueue是不能退出的。
  • 其次调用Looper.loop();开启循环

通过上面两个步骤开启了主线程的Hanlder机制

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容