Android Handler源码

目录

前言

故事简介

故事开始

        1.角色分配

        2.邮寄过程

        3.派发过程

        4.收货

后续

总结


前言

今天直接以讲故事的思路引导我们去看Handler的源码,希望能够加强大家的理解,留给我们的时间不多了,开始吧!


故事简介

经过我每天敲23个小时的代码,终于存够了一个亿,我决定邮寄回给我远在家乡的老婆,让她享福一下。至于为啥23个小时,那必须留一个小时跟我老婆视频。

故事开始

1.角色分配

  • Handler  快递员(快递小哥)
  • Looper   快递公司(快递公司)
  • MessageQueue (快递仓库)
  • Message 包裹(一个亿)   

2.邮寄过程

    今天15号,终于发了工资,也挣够了我的一个亿,我一想到远在家乡的老婆,我就心生幸福感,终于可以把凑齐的一个亿寄回给她了;于是23:00下了一个早班,回到家中从床底拉出了我一沓又一沓的现金,确定刚好为一个亿之后安心入睡了。

   早6:00便在小程序中下了快递订单,快递小哥也是勤奋努力,6:30就来了,帮我打包、搬运了两个小时,搬运完我支付了5000元的邮费,小哥开着卡车便扬长而去,把我的一个亿拉回了快递仓库进行了打包。

   那好,上面一段故事的场景中牵扯出了所有角色,那现在我们就进入Handler源码看下是如何演绎上述故事场景的。

Message message = Message.obtain();
message.obj = "一个亿";

Handler handler = new Handler();
handler.sendMessage(message);

这是我们常用的Handler发送消息的代码,那么我们就跟着sendMessage方法,看看是如何把一个亿运到仓库的。

/**
 * Pushes a message onto the end of the message queue after all pending messages
 * before the current time. It will be received in {@link #handleMessage},
 * in the thread attached to this handler.
 *  
 * @return Returns true if the message was successfully placed in to the 
 *         message queue.  Returns false on failure, usually because the
 *         looper processing the message queue is exiting.
 */
public final boolean sendMessage(@NonNull Message msg) {
   return sendMessageDelayed(msg, 0);
}

一直在内部调用自己的方法,最后调用到下面内部方法。

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

大家发现queue这个属性没有,其实它就是仓库MessageQueue,怎么证明呢?回到上一层调用enqueueMessage的方法中,局部变量queue是被Handler中全局变量mQueue赋值的。

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

我们再看下mQueue。

final MessageQueue mQueue;

mQueue确实是MessageQueue(仓库),那Handler(快递小哥)直接通过sendMessage方法就把我一个亿的Message(包裹)输送到仓库了。不过我们还是要再看一下MessageQueue(仓库)是在哪里建设的。

一.第一处
  /**
     * 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(@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;
    }



二.第二处
 /**
     * 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 conditions such as display vsync.
     *
     * @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
     */
    @UnsupportedAppUsage
    public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

总共有两处,可以看出第一处是Looper的静态方法获得Looper之后直接把Looper的mQueue赋值给了Handler的mQueue,第二处Looper是外部传入的后续操作一致,那可见MessageQueue(仓库)是在Looper(快递公司)中创建出来的,仓库属于快递公司符合常理。至于MessageQueue在Looper中也就是直接New出的MessageQueue对象。


    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

那Looper构造方法又是在哪里被调用的呢?

  /** 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);
    }

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

现在大家是否就能明白了,为啥在子线程中我们必须要调用prepare方法,需要创建Looper对象,也就是建立起Looper(快递公司),它才能帮我寄这Message(一个亿的包裹)

3.派发过程

Looper(快递公司)既然有了,那必须得启动运作,收了我的Message(一个亿的包裹)得给我送到老婆手里,那如何运作呢?

   /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    public static void loop() {
        final Looper me = myLooper();
           
        ...

        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);
                if (observer != null) {
                    observer.messageDispatched(token, msg);
                }
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } catch (Exception exception) {
                if (observer != null) {
                    observer.dispatchingThrewException(token, msg, exception);
                }
                throw exception;
            } finally {
                ThreadLocalWorkSource.restore(origWorkSource);
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            ...
        }
    }

loop方法中是一个死循环,会一直通过仓库MessageQueue的next方法获取Message(包裹),当拿到我的message(一个亿包裹),就会通过msg.target.dispatchMessage(msg)派发出去,这个target就是Handler(快递小哥),快递小哥会把message包裹(一个亿)送到我老婆手中。至于target,是在发送消息时,调用到enqueueMessage方法时就有赋值(见下面代码),就像快递小哥在揽收快递包裹之后入库肯定会标记揽件人为自己,只不过这里派件人也是需要同一个快递小哥。所以逻辑就是通过包裹上标记的target找到快递小哥,快递小哥再通过dispatchMessage(msg)分发派送。

  private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {

        msg.target = this;
        。。。
        return queue.enqueueMessage(msg, uptimeMillis);
    }

同时,通过上面我们也需要理解,为什么loop方法要在子线程中必须配合prepare调用,一个创建Looper(快递公司)一个启动公司运作,缺一不可。

4.收货

总的来说在经过了2天的时间,我老婆收到了我寄给她的一个亿,一早便收到了老婆的视频,老婆满脸笑容,我也很满足。收拾完下楼吃了一份热干面,高兴的去到公司努力挣下一个一亿,后面老婆在老家买了跑车,修了豪宅,过上了幸福的生活。

后续

因为我的这次邮寄一个亿对快递公司来说是一笔大单,我要求最快送达,那快递公司如何保证我的包裹最先配送的,它们如何排序的?下面我们进入到MessageQueue(仓库)中查找答案。

 boolean enqueueMessage(Message msg, long when) {
    ...

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

         ...
        }
        return true;
    }

上述代码中有一个when关键属性,这其实是一个long的时间戳,就是通过这个时间戳,message(包裹)在入仓库时都会指定一个时间,入库后就按着时间进行了排序。入库的处理找到了答案,我们再去看包裹是如何确保按顺序出库派送的。我们前面有了解到在Looper的loop方法中是调用MessageQueue的next获取message的,那我们直接去看next方法。

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

            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;
                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) {
                    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.
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

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

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

仔细看上面代码,可以发现也是一个死循环,通过now < msg.when这个条件判断当前的message的包裹时间是否大于当前时间,如果满足了这个条件,那就证明最近的这个message(包裹)还没到发出时间,那么就需要算出差值,在for循环的下一次执行时调用了下

nativePollOnce(ptr, nextPollTimeoutMillis)本地底层方法,进行阻塞等待一定的时间,然后再继续运行就会满足条件,成功返回message了。

总结

Handler作为快递员是收货和发货,也就是入口和出口;Looper是快递公司所以必须存在同时也得持续运行,所以必须调用prepare和loop方法,至于为啥我们在主线程中不用调用,那是因为ActivityThread中默认已经调用了。message是包裹同时也会包含一些信息,比如快递小哥的信息(Handler),MessageQueue作为仓库在Handler进行入库和出库时,通过when时间戳进行了排序来确保message的按时间发货。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值