Android--Handler机制及源码详解

429 阅读11分钟

主要的内容包括Handler的机制以及四个组成部分和源码的分析

下面的代码分析都是基于Android8.0 - Oreo的源代码

1. 消息机制简介

在应用启动时,会执行main()方法,main()会创建一个Looper对象,然后开启一个死循环,目的是不断从消息队列MessageQueue里面取出Message对象并处理。

在Android中使用消息机制,会优先想到的是Handler。Handler可以轻松的将一个任务切换到Handler所在的线程去执行。在多线程的应用场景中,可以将工作线程中需要更新UI的操作信息传递到主线程去执行,从而实现工作线程更新UI的操作,最终实现异步消息的处理。

Handler流程

2. Handler机制模型

消息机制主要包含HandlerMessageMessageQueueLooper这四个类。

  • Handler:消息辅助类。主要功能将Message对象发送到MessageQueue中,同时将自己的引用赋值给Message#target(Handler.sendMessage())。也可以实现handleMessage()方法处理回调。
  • Message:消息实体。需要传递的消息也可以传递数据。
  • MessageQueue:消息队列。**内部实现并不是队列,而是利用单链表去实现因为在插入和删除数据有优势。**用于存储Handler发给来的消息(Message)以及取出。内部使用单链表实现
  • Looper:与线程绑定,不止局限于主线程,绑定的线程来处理Message。不断循环执行Looper.loop(),从MessageQueue中读取Message,按分发机制将消息分发出去给目标处理(将Message发到Handler.dispatchMessage方法去处理)。

3. Handler运行流程

Handler运行流程
工作流程:异步通信准备==>消息入队==>消息循环==>消息处理

  1. 异步通信准备

    假定在主线程创建Handler,则会直接在主线程中创建Looper,MessageQueueHandler对象。Looper和MessageQueue对象均属于其创建线程(由主线程创建则属于主线程)。创建Looper时会自动创建MessageQueue对象,创建好MessageQueue对象后,Looper自动进入循环。Handler自动绑定Looper以及MessageQueue

    Looper对象的创建方法一般通过Looper.prepareMainLooper()Looper.prepare()方法。

  2. 消息入队

    工作线程通过Handler发送MessageMessageQueue中。消息内容一般是UI操作,通过Handler.sendMessage(Message message)Handler.post(Runable r)发送。加入MessageQueue一般通过MessageQueue.enqueueMessage(Message msg,long when)操作。

  3. 消息循环

    分为消息出队消息分发两个步骤

    • 消息出队:LooperMessageQueue中循环取出Message
    • 消息分发:Looper将取出的Message分发给创建消息的Handler

    消息循环过程中,MessageQueue为空,则线程堵塞

  4. 消息处理

    Handler接受发过来的Message并处理。

4. Handler使用过程的注意点

  1. 在工作线程中创建自己的消息队列时必须要调用Looper.prepare(),并且在一个线程中只可以调用一次,然后需要调用Looper.loop(),开启消息循环。

    在开发过程中基本不会调用上述方法,因为默认会调用主线程的Looper,然后一个线程中只能有一个Looper对象和一个MessageQueue。

  2. 要注意Handler可能引起的内存泄漏(在下面会介绍到为何会引发泄漏)。

    错误的写法:

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

    非静态的内部类和匿名内部类都会隐式的持有其外部类的引用,而静态内部类不会持有外部类的引用。

    正确的写法:

    继承Handler时候要么放在单独的类文件中,要么直接使用静态内部类。

    //需要在静态内部类中调用外部类时,可以直接使用  `弱引用`  进行处理
    private static final class MyHandler extends Handler{
        private final WeakReference<MyActivity> mWeakReference;
        public MyHandler(MyActivity activity){
             mWeakReference = new WeakReference<>(activity);
        }
        @Override
        public void handlerMessage(Message msg){
            super.handlerMessage(msg);
            MyActivity activity = mWeakReference.get();
        }
    }
    //调用方法
    private MyHandler mHandler = new MyHandler(this);
    

5. Handler源码解析

  1. 创建循环器对象(Looper)和创建消息队列对象(MessageQueue)

    创建Looper对象主要有两个方法:Looper.prepareMainLooper()Looper.prepare()

    创建MessageQueue对象方法:创建Looper对象时会自动创建MessageQueue

    // 源码位置:../core/java/android/os/Looper.java
    
    final MessageQueue mQueue;
    final Thread mThread;
    //Looper对象创建时会自动创建一个MessageQueue对象。
    private Looper(boolean quitAllowed) {
         mQueue = new MessageQueue(quitAllowed);
         mThread = Thread.currentThread();
        }
    
    //为当前线程(子线程)创建一个Looper对象 需要在子线程中主动调用该方法
    public static void prepare() {
            prepare(true);
        }
    
    private static void prepare(boolean quitAllowed) {
        //判断sThreadLocal是否为null,不为空则直接跑出异常 可以保证一个线程只可以调用一次prepare方法
        if (sThreadLocal.get() != null) {
              throw new RuntimeException("Only one Looper may be created per thread");
           }
        sThreadLocal.set(new Looper(quitAllowed));
        }
    
    //为主线程创建一个Looper对象 该方法会在主线程创建时自动调用
    public static void prepareMainLooper() {
            prepare(false);
            synchronized (Looper.class) {
                if (sMainLooper != null) {
                    throw new IllegalStateException("The main Looper has already been prepared.");
                }
                sMainLooper = myLooper();
            }
        }
    

    总结:

    1. 创建Looper对象时会自动创建MessageQueue对象

    2. 主线程的Looper对象是自动生成的,而子线程需要调用Looper.prepare()创建Looper对象

      创建主线程是调用了ActivityThreadmain()方法。

      然后按照流程调用了Looper.prepareMainLooper()Looper.loop()。所以主线程不需要调用代码生成Looper对象。

      //源码位置: ../core/java/android/app/ActivityThread.java
       public static void main(String[] args) {
           ...
            Looper.prepareMainLooper();
            Looper.loop();
           ...
       }
      
    3. Handler的主要作用是(在主线程更新UI),所以Handler主要是在主线程创建的

    4. Looper与Thread是通过ThreadLocal关联的。由于ThreadLocal是与线程直接关联的,参考prepare()

    5. 子线程创建Handler对象:无法在子线程直接调用Handler无参构造方法Handler创建时需要绑定Looper对象 。需要使用HandlerThread

  2. 开启Looper即消息循环

    创建了Looper和MessageQueue对象后,自动进入消息循环,使用Looper.loop()方法开始消息循环。

    //源码位置:../core/java/android/os/Looper.java
    public static void loop(){
        //现获取Looper实例,保证调用loop时已有Looper,否则抛出异常
        final Looper me = myLooper();
        if (me == null) {
                throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
           }
        //获取对应Looper实例创建的MessageQueue对象
        final MessageQueue queue = me.mQueue;
        ...
         //开启消息循环-无限循环   
         for (;;) {
                //从MessageQueue取出Message对象
                Message msg = queue.next(); // might block
                //取出消息为null,则退出循环
                if (msg == null) {
                    // No message indicates that the message queue is quitting.
                    return;
                }
             //把Message分发给相应的target
             try {
                    msg.target.dispatchMessage(msg);
                    end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
                } finally {
                    if (traceTag != 0) {
                        Trace.traceEnd(traceTag);
                    }
                }
             //释放消息占据的资源
              msg.recycleUnchecked();
         }
    }
    
    
  3. 创建Handler对象

    创建Handler对象即可以进行消息的发送与处理

    //源码位置:.../core/java/android/os/Handler.java 
    //Handler默认构造方法
    public Handler() {
            this(null, false);
     }
    
    public Handler(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());
                }
            }
            //从当前线程的ThreadLocal获取Looper对象
            mLooper = Looper.myLooper();
            if (mLooper == null) {
                throw new RuntimeException(
                    "Can't create handler inside thread that has not called Looper.prepare()");
            }
            //获取当前Looper的消息队列
            mQueue = mLooper.mQueue;
            mCallback = callback;
            //设置消息是否为异步处理方式
            mAsynchronous = async;
        }
    
    public Handler(Looper looper, Callback callback, boolean async) {
            mLooper = looper;
            mQueue = looper.mQueue;
            mCallback = callback;
            mAsynchronous = async;
        }
    
    

    Handler的无参构造方法会默认关联当前线程的Looper对象和MessageQueue对象,设置callback回调方法为null,且消息处理方式为同步处理。

  4. 创建消息对象

    Handler发送Message并且进入MessageQueue循环,创建方式分为两种new Message()Message.obtain()。通常使用Message.obtain()。这种方式有效避免创建重复Message对象

    //创建消息对象
    Message msg = Message.obtain();
    msg.what = 1;
    msg.obj = "test";
    
    //源码位置 .../core/java/android/os/Message.java
        /** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}).
        */
    //new Message 方法
    public Message() {
        }
    
    private static final Object sPoolSync = new Object();
    //维护一个Message池,用于复用Message对象
    private static Message sPool;
    //obtain方法 直接从池内获取Message对象,避免new占用内存
    public static Message obtain() {
            synchronized (sPoolSync) {
                if (sPool != null) {
                    Message m = sPool;
                    sPool = m.next;
                    m.next = null;
                    m.flags = 0; // clear in-use flag
                    sPoolSize--;
                    //直接从池中取出
                    return m;
                }
            }
            //无可复用对象,则重新new获取
            return new Message();
        }
    
  5. 发送消息(Message)

    Handler主要有以下几种发送消息的方式:

    • sendMessage(Message msg)
    • sendMessageDelayed(int what, long delayMillis)
    • post(Runnable r)
    • postDelayed(Runnable r, long delayMillis)
    • sendMessageAtTime(Message msg, long uptimeMillis)

    最终都是会调用到sendMessageAtTime(Message msg, long uptimeMillis)然后继续调用到enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)放入MessageQueue

    //源码位置:.../core/java/android/os/Handler.java 
    //post方法
    public final boolean post(Runnable r)
        {
           return  sendMessageDelayed(getPostMessage(r), 0);
        }
    public final boolean postAtTime(Runnable r, long uptimeMillis)
        {
            return sendMessageAtTime(getPostMessage(r), uptimeMillis);
        }
    public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
        {
            return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
        }
    public final boolean postDelayed(Runnable r, long delayMillis)
        {
            return sendMessageDelayed(getPostMessage(r), delayMillis);
        }
    //利用post()方式发送消息,需要转换为Message向下传递
    private static Message getPostMessage(Runnable r, Object token) {
            Message m = Message.obtain();
            m.obj = token;
            //将runnable赋值到callback上 以便后续判断是post还是sendMessage方式发送的消息
            m.callback = r;
            return m;
        }
    
    //sendMessage方法
    public final boolean sendMessage(Message msg)
        {
            return sendMessageDelayed(msg, 0);
        }
    
    public final boolean sendMessageDelayed(Message msg, long delayMillis)
        {
            if (delayMillis < 0) {
                delayMillis = 0;
            }
            return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
        }
    
    //所有的发送消息有关方法 都会调用到这个方法
    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
            //获取MessageQueue对象 
            MessageQueue queue = mQueue;
            //获取对象为空 抛出异常
            if (queue == null) {
                RuntimeException e = new RuntimeException(
                        this + " sendMessageAtTime() called with no mQueue");
                Log.w("Looper", e.getMessage(), e);
                return false;
            }
            //对象不为空 调用enqueueMessage方法
            return enqueueMessage(queue, msg, uptimeMillis);
        }
    
    //该方法为了 向MessageQueue插入Message
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
            // 把当前的Handler设置为 消息标记位 即把消息派发给相对应的Handler实例
            msg.target = this;
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
            //调用MessageQueue的enqueueMessage方法
            return queue.enqueueMessage(msg, uptimeMillis);
        }
    
    //源码位置:..core/java/android/os/MessageQueue.java
    //内部是一个单链表有序序列,由 Message.when 作为排序依据,该值为一个相对时间。
    boolean enqueueMessage(Message msg, long when) {
        ...
        synchronized (this) {
                //正在退出 回收Message
                if (mQuitting) {
                    IllegalStateException e = new IllegalStateException(
                            msg.target + " sending message to a Handler on a dead thread");
                    Log.w(TAG, e.getMessage(), e);
                    msg.recycle();
                    return false;
                }
    
                msg.markInUse();
                msg.when = when;
                Message p = mMessages;
                boolean needWake;
                // p == null判断当前队列中是否有消息,插入消息作为队列头 
                // when == 0||when < p.when 队列当前处于等待状态 唤醒队列
                if (p == null || when == 0 || when < p.when) {
                    // New head, wake up the event queue if blocked.
                    msg.next = p;
                    mMessages = msg;
                    needWake = mBlocked;
                } else {
                    // Inserted within the middle of the queue.  Usually we don't have to wake
                    // up the event queue unless there is a barrier at the head of the queue
                    // and the message is the earliest asynchronous message in the queue.
                    //当前队列有消息,按照消息创建时间插入到队列中
                    needWake = mBlocked && p.target == null && msg.isAsynchronous();
                    Message prev;
                    //从对列头部开始遍历
                    for (;;) {
                        prev = p;
                        p = p.next;
                        //循环到队列尾部或者出现一个when小于当前Message的when
                        if (p == null || when < p.when) {
                            break;
                        }
                        if (needWake && p.isAsynchronous()) {
                            needWake = false;
                        }
                    }
                    msg.next = p; // invariant: p == prev.next
                    prev.next = msg;
                }
    
                // We can assume mPtr != 0 because mQuitting is false.
                if (needWake) {
                    nativeWake(mPtr);
                }
            }
            return true;
      }
    
    
    

    总结:

    • 发送消息时Message.when表示期望该消息被分发的时间即SystemClock.uptimeMillis() + delayMillisSystemClock.uptimeMills代表自系统开机到调用到该方法的时间差。
    • Message.when利用时间差来表达期望事件分发的时间,所以使用的是一个相对时间。
  6. 获取消息

    发送了消息后,MessageQueue维护了消息队列,在Looper中通过loop()不断获取Message。通过next()获取Message.

     //源码位置:..core/java/android/os/MessageQueue.java
     Message next(){
     //该参数用于确定消息队列中是否有消息 下一个消息到来前需要等待的时长
       int nextPollTimeoutMillis = 0;
        for (;;) {
               if (nextPollTimeoutMillis != 0) {
                   Binder.flushPendingCommands();
               }
               //该方法位于native层 若nextPollTimeoutMillis为-1 代表消息队列处于等待状态 阻塞操作
               nativePollOnce(ptr, nextPollTimeoutMillis);
               ...
               synchronized (this) {
                   ...
                    Message msg = mMessages;
                    if (msg != null) {
                       if (now < msg.when) {
                           // Next message is not ready.  Set a timeout to wake up when it is ready.
                           nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                       } else {
                           // Got a message.
                           mBlocked = false;
                           if (prevMsg != null) {
                               prevMsg.next = msg.next;
                           } else {
                               mMessages = msg.next;
                           }
                           msg.next = null;
                           //标记消息使用状态 flag |= FLAG_IN_USE
                           msg.markInUse();
                           //返回一条消息
                           return msg;
                       }
                   } else {
                       // No more messages.
                       nextPollTimeoutMillis = -1;
                   }
                   //消息正在退出
                   if (mQuitting) {
                       dispose();
                       return null;
                   }
               }
         }
     }
    
    
  7. 分发消息

    分发消息到对应的Handler实例并根据传入的Message做对应的操作

    //源码位置:.../core/java/android/os/Handler.java 
    
      public void dispatchMessage(Message msg) {
            //若callback不为空,则代表使用了post(Runnable r)方式发送了消息,执行handleCallback方法
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                //代表使用了sendMessage()方式发送了消息,调用handleMessage方法
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                handleMessage(msg);
            }
        }
    
    //创建Handler实例时复写 自定义消息处理方法
    public void handleMessage(Message msg) {
        }
    
    //直接回调runnable对象的run()
    private static void handleCallback(Message message) {
            message.callback.run();
        }
    

    总结:

    • msg.target.dispatchMessage(msg)msg.target指向的就是对应Handler实例,
    • 消息分发的优先级:
      1. Message的回调方法message.callback.run()
      2. Handler中Callback的回调方法mCallback,handleMessage(msg)
      3. Handler的默认方法handleMessage()
  8. Message回收

    上面讲到了新建Message推荐使用obtain(),因为可以有效的复用消息,其中里面复用的就是sPool变量,它是在Message回收的时候进行赋值的。

    //源码位置 .../core/java/android/os/Message.java
    /*package*/ boolean isInUse() {
            return ((flags & FLAG_IN_USE) == FLAG_IN_USE);
        }
    
    public void recycle() {
            //正在使用 无法回收
            if (isInUse()) {
                if (gCheckRecycle) {
                    throw new IllegalStateException("This message cannot be recycled because it "
                            + "is still in use.");
                }
                return;
            }
            recycleUnchecked();
        }
    
    void recycleUnchecked() {
            // Mark the message as in use while it remains in the recycled object pool.
            // Clear out all other details.
            //置为使用标记
            flags = FLAG_IN_USE;
            what = 0;
            arg1 = 0;
            arg2 = 0;
            obj = null;
            replyTo = null;
            sendingUid = -1;
            when = 0;
            target = null;
            callback = null;
            data = null;
    
            //将Message放在了列表里,缓存的对象由obtain()拿出来复用
            synchronized (sPoolSync) {
                if (sPoolSize < MAX_POOL_SIZE) {
                    next = sPool;
                    sPool = this;
                    sPoolSize++;
                }
            }
        }
    
  9. Looper退出

    Looper.loop()内部由一个无限循环组成,默认情况下不会退出循环。需要退出就需要调用quit()或者quitSafely()

    //源码位置 .../core/java/android/os/Looper.java
        public void quit() {
            mQueue.quit(false);
        }
    
        public void quitSafely() {
            mQueue.quit(true);
        }
    
    //源码位置 .../core/java/android/os/MessageQueue.java
    void quit(boolean safe) {
            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.
                //唤醒等待线程
                nativeWake(mPtr);
            }
        }
    
    //直接移除MessageQueue中的所有消息
     private void removeAllMessagesLocked() {
            Message p = mMessages;
            while (p != null) {
                Message n = p.next;
                //回收未被处理的消息
                p.recycleUnchecked();
                p = n;
            }
            //由于消息为null 则return 出无限循环
            mMessages = null;
        }
    
    //直接移除未处理的消息 已经在执行的继续处理
    private void removeAllFutureMessagesLocked() {
            final long now = SystemClock.uptimeMillis();
            Message p = mMessages;
            if (p != null) {
                //还未处理的Message
                if (p.when > now) {
                    removeAllMessagesLocked();
                } else {
                    Message n;
                    for (;;) {
                        n = p.next;
                        if (n == null) {
                            return;
                        }
                        if (n.when > now) {
                            break;
                        }
                        p = n;
                    }
                    //不接收后续消息
                    p.next = null;
                    do {
                        p = n;
                        n = p.next;
                        p.recycleUnchecked();
                    } while (n != null);
                }
            }
        }