Android Handler消息机制03-Message源码学习

Posted 双木青橙

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android Handler消息机制03-Message源码学习相关的知识,希望对你有一定的参考价值。

1.关键机制说明

1.1分发机制

分发是在Looper.loop()会在for循环中不停地从Message Queue中取出消息,并调用消息对应的Handler对象的dispatchMessage方法,详细执行见源码
其对于处理Message三种方法,且有优先级:

  1. 当Message的回调方法不为空时,则回调方法msg.callback.run(),其中callBack数据类型为Runnable,否则进入步骤2;
  2. 当Handler的mCallback成员变量不为空时,则回调方法mCallback.handleMessage(msg),否则进入步骤3;
  3. 调用Handler自身的回调方法handleMessage(),该方法默认为空,Handler子类通过覆写该方法来完成具体的逻辑。
    /**
     * 处理系统消息,在Looper.loop(),当发现有消息时,会调用消息的目标Handler,执行此方法来分发消息
     */
    public void dispatchMessage(@NonNull Message msg) 
        if (msg.callback != null) 
            // 当Message存在回调方法时,回调msg.callback.run()方法
            handleCallback(msg);
         else 
            if (mCallback != null) 
                // 当Handler存在callback成员变量时,回调callback的handleMessage方法
                if (mCallback.handleMessage(msg)) 
                    return;
                
            
            // 执行Handler自身的回调方法handleMessage
            handleMessage(msg);
        
    

1.2 消息发送机制

在Handler 有各种类型的发送消息的机制,有实时发送(例如:sendMessage(@NonNull Message msg),延时相对时间发送(sendMessageDelayed(@NonNull Message msg, long delayMillis)),指定绝对时间发送(sendMessageAtTime(@NonNull Message msg, long uptimeMillis))等,但是实际上最终调用queue.enqueueMessage方法

需要重点注意的时间,延时是通过SystemClock.uptimeMillis() 加上延时时间来计算的。SystemClock 有一个特点是在手机休眠时间是不计时的,所以在后台执行时有可能会实际执行比指定延时时间delayMillis更长

2. 问题

  • Q: Handler 、MessageQueue、Looper和线程分别是什么关系?
    A: 一个线程是只有一个Looper,且也只对应一个MessageQueue,但是可以有多个Handler。Message对象会关联一个Handler对象,message.target,Handler也只会处理自己发送的消息。
    即Handler:MessageQueue:Looper:Thread = N:1:1:1;
  • Q:为什么实际延迟消息处理时间比指定时间更长。
    A:有两个原因,一个延迟处理是通过SystemClock.uptimeMillis() 来计算的,此方法在系统休眠时并不会计时。还有一个原因是队列中的消息是要逐个处理的,当消息队列中消息过多时也造成延时。
  • Q:HandlerMessage的使用场景及禁止使用的场景
    android系统中出于性能优化考虑,Android的UI操作并不是线程安全的,这意味着如果有多个线程并发操作UI组件,可能导致线程安全问题。为了解决这个问题,Android制定了一条简单的原则,只允许UI线程(亦即主线程)修改Activity中的UI组件,否则会抛出异常,但是我们经常将耗时操作(DB、网络在子线程进行操作),这也就需要通过HandlerMessage机制将消息从子线程切换到主线程。总结就是:在子线程做完耗时的逻辑处理后 将消息或者结果发送到主线程用于更新UI,其他场景都不建议大家使用HandlerMessage机制。
    禁止的场景是:子线程发送Message消息对象到主线程处理,又将消息对象因为耗时或者其他原因切换子线程来进行处理。这种会导致多线程安全问题。因为我们现在建议的方法都是通过obtainMessage从消息池获取,在Looper.loop()方法中,方法处理方法dispatchMessage和消息回收方法msg.recycleUnchecked(),也就是说如果你在dispatchMessage方法中将Message对象传递到子线程处理时,Message对象有可能被回收了,导致多线程安全问题。

3.源码

源码我将部分dump 和日志打印删节后全部贴上来,用于备查

package android.os;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.util.Log;
import android.util.Printer;

import java.lang.reflect.Modifier;

/**
 * Handler可以发送和处理@link Message对象,以及与线程的@link MessageQueue 关联的Runnable对象,
 * 每一个Hnadler 对象只能关联一个线程以及这个线程对应的Looper。当创建一个新的Handler,它将绑定到@link Looper。
 * Hanlder会将消息和Runnable对象传递到Looper中的消息队列,并且在Looper关联的线程中执行他们。
 * 即可以理解,Handler:Looper:Thread:MessageQueue=N:1:1:1
 *
 * Hnadler 有两个主要用途:1)调度Message和Runnable实现类对象在未来的某个时间点来执行。
 * 2)将耗时任务安排在其他线程执行而不是当前线程
 *
 * 当post或者send消息到Handler时,用户可以让消息当消息队列Ready后立马处理,也可以延迟指定时间或者绝对时间再处理。
 * 后面两次方法可以实现超时、或者其他基于计时的行为。
 *
 * 当应用的进程被创建后,主线程专用于一个Message queue,负责管理顶级应用对象(activities, broadcast receivers, etc)
 * 和创建的窗口。用户可以创建自己的子线程,然后通过Handler和主线程进行通信,实现方法是在子线程中调用post 或者sendMessage等方法,
 * 被传入的Runnable实现类对象和Message对象会被排入Handler中的Message Queue,然后在适当的时候进行处理
 */
public class Handler 
    @UnsupportedAppUsage
    final Looper mLooper;
    final MessageQueue mQueue;
    @UnsupportedAppUsage
    final Callback mCallback;
    final boolean mAsynchronous;
    @UnsupportedAppUsage
    IMessenger mMessenger;
    /*
     * 当此标志设备为true,以检测非静态的匿名内部类(继承于Handler),这种类可能会产生内存泄漏
     */
    private static final boolean FIND_POTENTIAL_LEAKS = false;
    private static final String TAG = "Handler";
    private static Handler MAIN_THREAD_HANDLER = null;

    /**
     * Callback interface you can use when instantiating a Handler to avoid
     * having to implement your own subclass of Handler.
     * 回调接口,用于实例化Handler避免不得不实现自己的Handler子类
     */
    public interface Callback 
        /**
         * @param msg A @link android.os.Message Message object
         * @return 如果不需要进一步处理,则为true
         */
        boolean handleMessage(@NonNull Message msg);
    

    /**
     * 子类必须实现他用于接收消息
     */
    public void handleMessage(@NonNull Message msg) 
    

    /**
     * 处理系统消息,在Looper.loop(),当发现有消息时,会调用消息的目标Handler,执行此方法来分发消息
     */
    public void dispatchMessage(@NonNull Message msg) 
        if (msg.callback != null) 
            // 当Message存在回调方法时,回调msg.callback.run()方法
            handleCallback(msg);
         else 
            if (mCallback != null) 
                // 当Handler存在callback成员变量时,回调callback的handleMessage方法
                if (mCallback.handleMessage(msg)) 
                    return;
                
            
            // 执行Handler自身的回调方法handleMessage
            handleMessage(msg);
        
    

    /**
     * 默认构建方法,将当前线程的@link Looper 与此Handler对象进行关联
     *
     * 如果当前线程没有Looper,此Handler没有办法去接收消息,所以会抛出Exception
     *
     * @deprecated 在构建Handler时隐式指定Looper会导致一些错误,当Handler关联的线程不是用户想要的。比如:operations丢失(如果Hnadler没有接收新任务并退出),
     * Crashes(如果Handler不小心在构建时关联了一个没有Active Looper对象的线程),或者race Condition
     * 相反地,使用@link java.util.concurrent.Executor,或者通过
     * @link Looper#getMainLooper, link android.view.View#getHandler或者类似的方法 显式指定Looper,
     * 为了兼容当前线程的隐式构建方法,使用@code new Handler(Looper.myLooper())
     *
     */
    @Deprecated
    public Handler() 
        this(null, false);
    

    /**
     * Constructor associates this handler with the @link Looper for the
     * current thread and takes a callback interface in which you can handle
     * messages.
     * 构造方法将Handler 与当前线程的Looper进行关联,并指定一个可以处理消息的callback接口
     *
     * @param callback 用于处理消息的Callback接口,也可以为空
     **/

    @Deprecated
    public Handler(@Nullable Callback callback) 
        this(callback, false);
    

    public Handler(@NonNull Looper looper) 
        this(looper, null, false);
    

    public Handler(@NonNull Looper looper, @Nullable Callback callback) 
        this(looper, callback, false);
    

    @UnsupportedAppUsage
    public Handler(boolean async) 
        this(null, async);
    

    /**
     * 指定Looper为当前线程的Looper且为设置此Handler是否为异步,以及指定一个处理消息Callback接口
     *
     * 默认情况下,Handler是同步的,除非使用该构造函数指定为异步的
     *
     * 异步消息表示不需要相对于同步消息进行全局排序的中断或事件。
     * 异步消息不受@link MessageQueue#enqueueSyncBarrier(long)引入的同步barriers的影响。
     *
     * @param callback 用于处理消息的callback接口实现类,也可为空
     * @param async 如果为true,则handler会
     * 对于每一个他分发的Message对象或者Runnable实现类都调用调用@link Message#setAsynchronous(boolean)
     *
     * @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) 
            // 也即意味着在调用Handler方法前,必要调用先调用Looper.prepare()
            throw new RuntimeException(
                    "Can't create handler inside thread " + Thread.currentThread()
                            + " that has not called Looper.prepare()");
        
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    

    /**
     *
     * @param looper The looper, 不允许为空
     * @param callback 用于处理消息的callback接口实现类,也可为空
     * @param async 如果为true,则handler会
     * 对于每一个他分发的Message对象或者Runnable实现类都调用调用@link Message#setAsynchronous(boolean)
     *
     * @hide
     */
    @UnsupportedAppUsage
    public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) 
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    

    @NonNull
    public static Handler createAsync(@NonNull Looper looper) 
        if (looper == null) throw new NullPointerException("looper must not be null");
        return new Handler(looper, null, true);
    

    /**
     * 创建一个新的Handler,posted 的消息和Runnables对象不受同步barriers的约束,例如display vsync
     *
     * <p>Messages sent to an async handler are guaranteed to be ordered with respect to one another,
     * but not necessarily with respect to messages from other Handlers.</p>
     *
     * @see #createAsync(Looper) to create an async Handler without custom message handling.
     *
     * @param looper the Looper that the new Handler should be bound to
     * @return a new async Handler instance
     */
    @NonNull
    public static Handler createAsync(@NonNull Looper looper, @NonNull Callback callback) 
        if (looper == null) throw new NullPointerException("looper must not be null");
        if (callback == null) throw new NullPointerException("callback must not be null");
        return new Handler(looper, callback, true);
    

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

    /** @hide */
    @NonNull
    public static Handler mainIfNull(@Nullable Handler handler) 
        return handler == null ? getMain() : handler;
    

    /**
     * 返回一个表示指定消息名称的字符串。默认实现要么返回* message回调的类名(如果有的话),要么返回* message“what”字段的十六进制表示。
     *
     * @param message 需要查询名称的消息对象
     */
    @NonNull
    public String getMessageName(@NonNull Message message) 
        if (message.callback != null) 
            return message.callback.getClass().getName();
        
        return "0x" + Integer.toHexString(message.what);
    

    /**
     * 从消息池中返回一个新的Message对象 @link android.os.Message Message .
     * 创建的消息会将此Handler设置为此实例(Message.target == this).
     *  也可以通过Message.obtain()来代替
     */
    @NonNull
    public final Message obtainMessage()
    
        return Message.obtain(this);
    

    @NonNull
    public final Message obtainMessage(int what)
    
        return Message.obtain(this, what);
    

    @NonNull
    public final Message obtainMessage(int what, @Nullable Object obj) 
        return Message.obtain(this, what, obj);
    

    @NonNull
    public final Message obtainMessage(int what, int arg1, int arg2)
    
        return Message.obtain(this, what, arg1, arg2);
    

    @NonNull
    public final Message obtainMessage(int what, int arg1, int arg2, @Nullable Object obj) 
        return Message.obtain(this, what, arg1, arg2, obj);
    

    /**
     * 将Runnable实现类添加到消息对队中,Runnable实现类在在Handler关联的线程中执行。
     *
     * @param r The Runnable that will be executed.
     * @return 当Runnable实现类对象r成功添加到消息队列中返回true,失败时则返回false,通常是因为处理消息队列的Looper不存在
     */
    public final boolean post(@NonNull Runnable r) 
        return  sendMessageDelayed(getPostMessage(r), 0);
    

    /**
     * 将Runnable实现类添加到消息对队中,其将会在uptimeMillis指定的特定时间执行,手机休眠会增加执行的额外延迟,
     * Runnable实现类在在Handler关联的线程中执行。
     *
     * @param r The Runnable that will be executed.
     * @param uptimeMillis 回调执行的绝对时间,(并非相对时间),使用@link android.os.SystemClock#uptimeMillis作为基础时间
     */
    public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) 
        return sendMessageAtTime(getPostMessage(r), uptimeMillis);
    

    /**
     * Causes the Runnable r to be added to the message queue, to be run
     * at a specific time given by <var>uptimeMillis</var>.
     * 将Runnable实现类添加到消息对队中,其将会在uptimeMillis指定的特定时间执行,手机休眠会增加执行的额外延迟,
     * Runnable实现类在在Handler关联的线程中执行。
     *
     * @param r The Runnable that will be executed.
     * @param token 可以通过此Token 通过@link #removeCallbacksAndMessages取消@code r的实例
     * @param uptimeMillis 回调执行的绝对时间,(并非相对时间),使用@link android.os.SystemClock#uptimeMillis作为基础时间
     *
     * @return 当Runnable实现类对象r成功添加到消息队列中返回true,失败时则返回false,通常是因为处理消息队列的Looper不存在
     * 注意:返回true并不代表Runnable会被执行,比如如果Looper被quit()在消息被分发时,会导致队列中的所有消息都被丢弃
     *
     * @see android.os.SystemClock#uptimeMillis
     */
    public final boolean postAtTime(
            @NonNull Runnable r, @Nullable Object token, long uptimeMillis) 
        return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
    

    public final boolean postDelayed(@NonNull Runnable r, long delayMillis) 
        return sendMessageDelayed(getPostMessage(r), delayMillis);
    

    /** @hide */
    public final boolean postDelayed(Runnable r, int what, long delayMillis) 
        return sendMessageDelayed(getPostMessage(r).setWhat(what), delayMillis);
    

    public final boolean postDelayed(
            @NonNull Runnable r, @Nullable Object token, long delayMillis) 
        return sendMessageDelayed(getPostMessage(r, token), delayMillis);
    

    /**
     * 将发送一个Runnable实现类到队列队首,消息将在下一次循环时首先执行
     * 此方法仅用于非常特殊的情况下使用,它会带来排序问题以及其他副作用
     **/
    public final boolean postAtFrontOfQueue(@NonNull Runnable r) 
        return sendMessageAtFrontOfQueue(getPostMessage(r));
    

    /**
     * 同步运行指定的任务(同步方法)(危险方法)
     * <p>
     * 如果当前线程是Handler关联线程,则Runnable会立马执行,否则会将Runnables 发送至Handlersr执行且等他执行完成
     **/
    public final boolean runWithScissors(@NonNull Runnable r, long timeout) 
        if (r == null) 
            throw new IllegalArgumentException("runnable must not be null");
        
        if (timeout < 0) 
            throw new IllegalArgumentException("timeout must be non-negative");
        

        if (Looper.myLooper() == mLooper) 
            r.run();
            return true;
        

        BlockingRunnable br = new BlockingRunnable(r);
        return br.postAndWait(this, timeout);
    

    /**
     * 删除所有消息队列中待等待执行的Runnable实现类对象
     */
    public final void removeCallbacks(@NonNull Runnable r) 
        mQueue.removeMessages(this, r, null);
    

    /**
     * 删除所有带有token的等待执行的Runnable实现类对象,如果token为null,则所有的callbacks将会被删除
     */
    public final void removeCallbacks(@NonNull Runnable r, @Nullable Object token) 
        mQueue.removeMessages(this, r, token);
    

    public final boolean sendMessage(@NonNull Message msg) 
        return sendMessageDelayed(msg, 0);
    

    public final boolean sendEmptyMessage(int what)
    
        return sendEmptyMessageDelayed(what, 0);
    

    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) 
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    

    public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) 
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageAtTime(msg, uptimeMillis);
    

    /**
     * 发送一个延迟delayMillis ms的消息到队列中
     **/
    public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) 
        if (delayMillis < 0) 
            delayMillis = 0;
        
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    

    /**
     * 在绝对时间uptimeMillis ms时将一个消息对象添加到消息队列队列中,(注意是绝对时间)
     * 基础时间是指调用时的@link android.os.SystemClock#uptimeMillis.如果手机进入休眠状态,将会增加额外的时间延迟。
     *
     * @param uptimeMillis 消息应该投递至消息队列中的绝对时间,使用@link android.os.SystemClock#uptimeMillis作为基础时间
     * @return 当Runnable实现

以上是关于Android Handler消息机制03-Message源码学习的主要内容,如果未能解决你的问题,请参考以下文章

Android的消息机制Handler

深入分析Android-Handler消息机制

Android Handler消息机制源码解析

深入解析Android中Handler消息机制

Android的消息机制Handler详解

Android中的Handler机制