#再论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}.     *     * 

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. * *

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发送消息的方式


除了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("");// -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的时候就会自动关联;二,传入其它线程的Handler,比如主线程的handler。

  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只充当一个回调的作用。

总结

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

更多相关文章

  1. Android中View的绘制机制
  2. Android的BUG(一) - HTML 5 播放streaming video造成卡住的问题
  3. Android中Toast显示时间的自定义
  4. 在 Android(安卓)上,一个完整的 UDP 通信模块应该是怎样的?
  5. Android(安卓)中的线程池
  6. UI效果(9): 屏幕相关操作
  7. Android异步任务的使用方法
  8. Android(安卓)黑科技保活实现原理揭秘
  9. 一起来学Android(安卓)Studio:(五)导入 jar 包和第三方开源库方法

随机推荐

  1. jQuery:常用 jQuery方法,$()的四种类型参
  2. 虚拟文件系统
  3. 简化开发|Lombok神器带你消除冗余代码
  4. 紫枫术河 imx6 uboot的mtd分区总结(rootfs
  5. Uboot启动参数说明
  6. uboot的常用命令及用法
  7. 全面解析|搞懂Nginx这一篇就够了
  8. GNU Makefile手札
  9. 超级详细|Linux系统下从0到1的玩法大全
  10. buildroot管理uboot+kernel+rootfs