再论Handler—Handler事件分发、Handler线程切换原理解析

#再论Handler—Handler原理解析

标签(空格分隔): Android架构师之路


Handler,无论你菜鸟还是像我一样的老菜鸟,都无法避免这个亘古不灭的话题,只有了解学会Handler的原理,你才能变成一个出的厅堂下得厨房的优秀的老菜鸟。其实同类优秀的帖子已经很多了,但是为了强化记忆我的理解,还是想再次记一下,我心中的Handler。

要介绍Handler,就必须要介绍与它息息相关甚至“同为一体”的其它几个类:Looper、MessageQueue、Message。所以本篇文章就分为三个部分:

  • Handler、Looper、MessageQueue、Message原理及关系。
  • Handler事件分发原理。
  • Handler线程切换原理。

1. Handler、Looper、MessageQueue、Message原理及关系。

1.1 Message

Message是定义一个包含描述和可以发送到Handler的任意数据对象的消息。

我们先看Message的几个关键变量:
在这里插入图片描述
target,就是负责发送且分发它的Handler;
callback,是一个Runnable回调;
next,下一个消息池中的它之下的message。

现在有3个问题:

  • 1.target从哪来来,作用是什么?
  • 2.callback又是什么及其作用;
  • 3.还有next是什么?

我们先不着急给出答案,接着往下看。

1.2 MessageQueue 消息队列

它持有并维护了当前线程的Looper分发的消息列表。消息并不是直接而是通过Handler 关联的Looper入队列的。

MessageQueue看上去是消息队列,然而它实际上只是消息队列的管理者。消息列表是它持有的Message mMessage,是由Message组成的单链表结构。
我们看它管理消息的两个核心方法:

  • boolean enqueueMessage(Message msg, long when) ;
  • Message next()。
1.2.1 boolean enqueueMessage(Message msg, long when) ,消息入队列;

先上代码:

 boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
    

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

可以看到,在第一行就校验了msg.target是否为null,由此可知,msg必须关联(持有)负责分发它的handler;
最重要的是for循环里入队的这部分,通过逐个和消息列表里消息的发生时间对比,找到与当前入队的message的 -分发时间-之前它时间最接近的preMsg,将mesage设置到preMsg的next(此处也解答了问题3:message的next是什么),最终得到的是一个按时间先后顺序排列的链表;
此外,入队操作还做了一件事情:当消息入队时候,如果队列为空的或需要立即分发当前消息分发时间-早于消息队列中最近一个需要分发的消息的时间的(而此时MessageQueue的next()方法的循环是处于阻塞状态的),就调用一个native方法nativeWake()唤醒取出消息的next()(next方法的循环会被nativePollOnce()native方法阻塞)循环。

1.2.2 Message next();

先上源码

 Message next() {
        // Return here if the message loop has already quit and been disposed.
        // This can happen if the application tries to restart a looper after quit
        // which is not supported.
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
              //-1-直接休眠一定的时间;
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                  //-2-找出下一个需要分发的消息。
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    //-3- 如果时机未到,即当前时间还不到消息的处理时间,就计算休眠时间,
                    //反之,返回这个待处理的消息。并将休眠状态mBlocked置为false
                    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;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }

                // If first time idle, then get the number of idlers to run.
                // Idle handles only run if the queue is empty or if the first message
                // in the queue (possibly a barrier) is due to be handled in the future.
                //-4-只有当消息队列是空的或者现在还不到待处理的消息的时间的时候,idleHanler(空闲handler)才会被执行
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                //-5-如果没有空闲Handler待处理(消息队列也没有需要处理的message),那么,标记一下循环被阻塞了。
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }
                //-5-将idleHandler列表转为数组保存
                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.

            //-6-开始处理IdleHandler的事务。
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler

                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }

                //-7-如果IdleHandler的queueIdle返回false,就不需要保持,这执行完一次任务后直接移除,反之,
                //后面每次空闲的时候都执行 
                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;

            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            nextPollTimeoutMillis = 0;
        }
    }

看到这段代码,愈发觉得谷歌工程是真的是厉害,不知道秃到什么程度才能写出这么精妙的代码,哈哈哈哈哈😃。由于这段代码我看着太喜欢了,就按照注释逐个解释:

  • 1-这段代码上来不管三七二十一就调了native的休眠方法——nativePoolOnce()(正好对应了消息入队的nativeWake()),如果没有消息处理的时候,next()方法就会阻塞在这里。当然休眠时间nextPollTimeoutMillis为0的时候眼睛一闭一睁也没有意义啊。

此时,突然有一个问题——循环阻塞在这里了,主线程不就卡住了嘛?为什么没有ANR?

具体原理,可以查看这篇帖子还有这篇帖子等等很多优质讲解。简而言之调用native的nativePoolOnce方法时候,调用了linux低层,使当前线程腾出CPU的使用权,处于休眠等待状态,直到休眠时间结束或消息入队被唤醒。

  • 2- 通过内循环从消息队列中寻找出下一个有效的message;
  • 3- 判断是否到了上一步得到的message的处理时间,如果到了时间,就返回这个message(返回给Looper的调用处,然后分发该事件);如果没有到,计算需要休眠的时间。
  • 后面几点其实不在我们讨论内容,可以直接跳过
  • 4- 在3处,没有返回message,就以为着:1.要么现在还没有到下一个消息分发的时间;2.要么当前消息队列是空的。此时开始处理空闲handler(IdleHanler)的事物。
  • 5- 这一步将IdleHandler列表转为数组存储。为什么要转数组,而不是直接处理列表?此处我需要引入一段代码:
private IdleHandler[] mPendingIdleHandlers;
private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();

    /**
     * Add a new {@link IdleHandler} to this message queue.  This may be
     * removed automatically for you by returning false from
     * {@link IdleHandler#queueIdle IdleHandler.queueIdle()} when it is
     * invoked, or explicitly removing it with {@link #removeIdleHandler}.
     *
     * <p>This method is safe to call from any thread.
     *
     * @param handler The IdleHandler to be added.
     */
    public void addIdleHandler(@NonNull IdleHandler handler) {
        if (handler == null) {
            throw new NullPointerException("Can't add a null IdleHandler");
        }
        synchronized (this) {
            mIdleHandlers.add(handler);
        }
    }

    /**
     * Remove an {@link IdleHandler} from the queue that was previously added
     * with {@link #addIdleHandler}.  If the given object is not currently
     * in the idle list, nothing is done.
     *
     * <p>This method is safe to call from any thread.
     *
     * @param handler The IdleHandler to be removed.
     */
    public void removeIdleHandler(@NonNull IdleHandler handler) {
        synchronized (this) {
            mIdleHandlers.remove(handler);
        }
    }

可以看到,这个列表拥有add和remove的方法,这两个方法会在外部随时被调用,也就意味着,列表是变化的,如果一直添加的话,就会不停的处理,当前循环就会在这里耗时,消息队列的消息就有可能不能被及时处理了。

  • 6- 正式开始迭代处理idleHandler
  • 7- 我们先引入IdleHandler代码:
    /**
     * Callback interface for discovering when a thread is going to block
     * waiting for more messages.
     * 当线程即将进入阻塞状态的时候需要处理的更多消息
     */
    public static interface IdleHandler {
        /**
         * Called when the message queue has run out of messages and will now
         * wait for more.  Return true to keep your idle handler active, false
         * to have it removed.  This may be called if there are still messages
         * pending in the queue, but they are all scheduled to be dispatched
         * after the current time.
         * 当MessageQueue的消息被执行完或下一个消息需要等待时间的时候会被调用。
         * 如果仅需要被调用一次,返回false,则被调用一次就会从列表中移除此项,如果需要每次空闲
         * 时都需要被调用就返回true,则不会被移除。
         */
        boolean queueIdle();
    }

IdleHandle是当线程空闲(MessageQueue中暂时不处理消息时)时才需要处理的事物。使用方法使用场景我不再赘述。

  • 7-总之,如果IdleHandler的queueIdle()放回fasle,就在执行完之后移除掉此项,反之不会被移除,且下一次线程空闲的时候还会被调用。

好了,next()方法分析完了,再次揪一揪自己浓密健壮的头发感叹——谷歌工程是真的厉害!

1.3 Looper

Looper短短不到500行的代码,并没有什么特殊之处,它用于为一个线程运行一个消息循环。新的线程默认是没有消息循环的,在线程中调用prepare可以产生一个消息循环,然后调用loop方法可以开始运行循环直到线程终止。
Looper中有以下几个重要的方法:

  • ThreadLocal sThreadLocal ;
  • prepare();
  • loop();
1.3.1 sThreadLocal

sThreadLocal 存放了当前线程绑定的Looper,除非先调用了prepare(),否则调用ThreadLocal的get方法时会返回null;

1.3.2 prepare()

那么我们看看prepare()方法:

	
	/** 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()}.
      *初始化当前线程的Looper,这给我们一个机会去创建引用这个Looper       *的Handler,要真正开始循环,确保调用了loop方法。
      */
    public static void prepare() {
        prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }
    /**
    *Looper构造函数
    */
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

从代码及其注释可以看出来,它为当前线程初始化一个唯一(多次调用该方法会抛错)的Looper,并保存到sThreadLocal中,同时,会在其构造函数中,初始化一个唯一的MessageQueue。

1.3.3 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;

        //……

        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            //……
            try {
            	//事件分发
                msg.target.dispatchMessage(msg);
                //……
            } catch (Exception exception) {
               //……
                throw exception;
            } finally {
              //……
            }
          

            msg.recycleUnchecked();
        }
    }

这个方法的作用十分简单:
维持一个死循环。在循环中不断的从当前线程的MessageQueue中取出一个待分发的消息。

Message msg = queue.next(); // might block

注意这行代码,表示循环在这里可能会阻塞。当消息队列中没有消息的时候,循环就会阻塞在MessageQueue的next方法中(对应MessageQueue的next())。

然后调用message.target.dispatchMessage()方法执行事件。

1.4 Handler

1.4.1 Handler的构造方法

在这里插入图片描述
这几个构造函数分为两类:一类是形参有Looper的构造函数,一类是没有Looper的构造函数。

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;
    }
  1. 没有Looper的构造函数或无参的构造函数被调用时,默认绑定当前线程的Looper,这个Looper存于Looper的sThreadLocal中(注意这个不一定存在),并且会校验Looper是否存在,如果不存在,就会抛出异常.
 public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

2.而有Looper的构造函数则关联传入的Looper,而这一点与Handler的线程切换至关重要。

  public interface Callback {
        /**
         * @param msg A {@link android.os.Message Message} object
         * @return True if no further handling is desired
         */
        boolean handleMessage(@NonNull Message msg);
    }

另外,我们还需要注意构造函数中的Callback,初始化的时候也可以选择性传入,具体作用,我们后面讲

1.4.2 Handler发送消息的方式

Handler的方法

在这里插入图片描述
除了postAtFrontOfQueue方法和sendMessageAtFrontOfQueue方法,其它方法最后都调用了sendMessageAtTime方法。不过殊途同归,最终这三个方法都调用了enqueueMessage方法,最终调用到了当前线程的MessageQueue的enqueMessage方法

 private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

看到这段代码,我们看到一个熟悉的东西——target,不管是post还是send方式,最终都会在这里将当前发送该message的handler注入到message中,这样,就解答了问题1:message的target从哪里来的
在此有必要说明一点post方式和send方式一个不同之处——post方式发送的message会被注入我们初始化的Runnable回调,这就解答了我们前面的问题2:message的callback从哪里来的?



Handler,Message,MessageQueue,Looper的相关知识已经介绍完毕,下面开始正文了!

2. Handler事件分发原理

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

        // Install selective syscall interception
        AndroidOs.install();

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

        // 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>");
		
		// -1-初始化Looper
        Looper.prepareMainLooper();

        // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
        // It will be in the format "seq=114"
        long startSeq = 0;
        if (args != null) {
            for (int i = args.length - 1; i >= 0; --i) {
                if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                    startSeq = Long.parseLong(
                            args[i].substring(PROC_START_SEQ_IDENT.length()));
                }
            }
        }
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);

        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);
        //-2-开始循环
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

每每看到这段代码,我都有同样的感触。那是16年的初夏,就像我16岁的雨季,在android成长路上充满了懵懂和无知。当时面试的时候有一位面试官问题,android有没有main方法,我说有的,我看到过,她当时像笑傻子一样。那时候我还对android知之甚少,只知道初次看activity代码的时候,找不到main方法我就郁闷怎么启动呢,百度了一下找到了这个main方法……面试的时候我还和面试官争辩了几句……时至今日,细节我已经忘记,但是我还是想起了她,因为她笑起来可好看,而今天是七夕😿……

哈哈哈,言归正传,我们接着说这个方法。

step1 初始化Looper

在ActivityThread类的main方法中:
Looper调用了它的一个特殊的方法——prepareMainLooper,专门为主线程准备的,用于初始化主线程的Looper;初始化完毕后,调用Looper的loop方法,启动Looper,的loop,开始消息分发。

step2 初始化一个用于事件分发的Handler

结合我们前面讲到的,handler没有Looper的构造函数调用的时候,会选择性注入一个callback;同时会默认绑定当前线程的Looper。这也就是为什么我们在主线程可以new Handler()然后直接发送消息的原因。

而在子线程,若像下面这种方式:

 new Thread(new Runnable() {
            @Override
            public void run() {

                new Handler().post(() -> {

                    // TODO: 2020-08-25 todo sth 
                });

            }
        }).start();

一运行显然是会失败的,会抛出 “Can’t create handler inside thread ThreadName-x that has not called Looper.prepare()”。我们前面讲Handler构造函数的时候说过,初始化Handler的时候需要关联一个Looper.子线程并没有初始化Looper,怎么办呢?有两种方式:一,我们自己调用Looper的prepare方法初始化一个,然后初始化Handler的时候就会自动关联;二,传入其它线程的Looper,比如主线程的Looper。

  public void startNewThread() {
        //仅用于示例,不要模仿我这种Thread写法
        new Thread(new Runnable() {
            @Override
            public void run() {
                //方式一
                Looper.prepare();
                new Handler(getMainLooper()).post(() -> {

                    // TODO: 2020-08-25 todo sth
                });
                Looper.loop();
//                Looper.myLooper().quitSafely();
                //方式二
                new Handler(getMainLooper()).post(() -> {

                    // TODO: 2020-08-25 todo sth
                });

            }
        }).start();
    }

切记!调用完Looper.prepare()方法后记得调用Looper.loop()方法。另外,在子线程任务结束后,调用 Looper.myLooper().quitSafely()退出。

step3 发送一个消息

我们通过post方式发送一个消息,紧接着在后续方法中给message注入当前handler,然后通过当前Handler关联的Looper,获取当关联的Looper中的MessageQueue,再然后,调MessageQueue的enqueueMessage方法消息入队。

step4 分发一个消息

消息入队之后,我们就该分发消息了。

我们再次定位到Looper的loop()方法中:

for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            //……
            try {
            	//事件分发
                msg.target.dispatchMessage(msg);
                //……
            } catch (Exception exception) {
               //……
                throw exception;
            } finally {
              //……
            }
        }

在loop循环中,通过MessageQueue的next方法取出待分发的消息,然后调用
msg.target.dispatchMessage(msg);上面我们已经知道,target就是message被发出入队时被注入的Handler,也就是说,负责发出这个message消息的handler,还负责分发它!我们走进Handler的dispathchMessage方法:

    
    /**
     * Handle system messages here.
     */
    public void dispatchMessage(@NonNull Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
    
    private static void handleCallback(Message message) {
        message.callback.run();
    }
    
    
      /**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(@NonNull Message msg) {
    }

第1部分:如果msg的calback存在,即通过post方式初始化注入的Runnable存在,那么调用这个Runnable处理消息;
第2部分:如果mCallback存在即初始化Handler的时候初始化了Callback,就调用mCallback的方式处理message。
第3部分:如果前两个回调都不存在,就调用Handler的handleMessage方法处理消息。而我们也看到了,这个方法是一个空的实现,这就需要我么自己去实现它。这也是为什么我们平时使用handler的时候经常会实例化Handler的子类而不是Handler本身的原因。

– 到此为止,Handler的事件分发原理终于讲完了……

3. Handler的线程切换原理。

我之前看过很多关于Handler的帖子,几乎都是在讲handler事件分发,本质上,事件分发和线程切换原理是一样的,但是,两者侧重点不同!两者都包含了消息机制的整个流程,很多人在讲述的时候,也把整个过程混为一谈;也有人把线程切换错误的认为是由handler完成的,实际上他是一叶障目只看到了冰山一角,根本没有看到重点,线程切换的实质和Handler根本没有半毛钱关系!!

**线程切换并不是由Handler完成的,线程切换的重点是Looper!**非要说和Handler有关系,那么只能说Handler只充当一个送快递的作用。

接下来,我来为你细细分解:

我们回溯从handler、looper的初始化到发出一个message,最后到message分发完成整个流程:

  • 创建一个Looper,内部初始化一个MessageQueue,运行looper;
  • 初始化一个Handler;
  • 发送一个消息,消息入队;
  • 消息出队,分发一个消息;

在讲Handler的构造函数时,我们知道,Handler会默认关联当前线程的Looper或传入的Looper,这点至关重要!为什么至关重要呢,因为:传入的不同线程的Looper,发送的消息最终被分发到的线程就不同!Message最终被处理的线程就是负责发送和分发它的Handler初始化的时候关联的Looper所在的线程!

是不是有点郁闷,我们接着往下捋:

handler通过post或者send方式最终都调用了下面两个方法:


    public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
        //消息队列
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }

    public final boolean sendMessageAtFrontOfQueue(@NonNull Message msg) {
        //消息队列
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, 0);
    }
    

没错,这就是前面说的入队的方法。但是你有没有一瞬间产生过疑问——mQueue从哪里来的???

往上翻,定位到Handler的两个构造方法,赫然看到一行:

mQueue = mLooper.mQueue;

看到没有,Handler的MessageQueue来自关联的Loooper,也就是我们前面说的,初始化Looper的时候默认初始化的MessageQueue!再回想我上面说的,Looper的prepare()方法为每个线程初始化一个唯一的Looper和MessageQueue!

到此,有没有想到什么?

我们再沿着流程往下,消息入队,然后由Looper的loop取出消息,最后被分发!
可以说,**所谓线程的切换,就是把消息插入不同线程的Looper内的MessageQueue中!**消息分发就是执行在不同线程的Looper的loop()方法中的,自然而然的,消息被处理的时候就已经在不同的线程中了。自此,线程也就切换完成了!而handler,开始是一个负责将消息送入队列的快递员,最后,又只起了一个回调的作用!

到此,Handler线程切换哦不,Looper的线程切换原理已经讲完了,最后总结两点:

  • 线程切换和事件分发本质是一样的;
  • 线程切换是有Looper完成的,Handler只充当一个回调的作用。

总结

好了,大功告成,这篇帖子到此为止了。描述顺序和逻辑可能有点乱,还请见谅。如有不足,欢迎批评指正,非常感谢:)。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值