Handler通信-源码分析

仍然记得刚开始学习android时,一直使用handler发送消息更新UI,只知道handler可以把消息发送到主线程。在后来随着工作经验的积累,发现Hanldler在整个Android系统中的角色举足轻重,它的产生并不只是用于UI处理,而更多的是handler是整个app的通信框架,我们可以在ActivityThread里面感受到,整个app都是用它来进行线程间的协调。

往事回忆

时至今天我仍然记得,刚出来那会去面试,面试官问:你了解handler吗?那我肯定回答:了解啊。接下来他问:在子线程中创建handler要注意什么问题?这我就有点蒙了,我是真不知道,因为从没在子线程中创建过handler,包括在后来的工作中,我从没在子线程中创建handler。当时在回去的路上,我觉得这面试官有点装,因为跟我扯了很多系统源码,恰恰我根本不懂,后来随着工作经验的积累,慢慢明白,当初别人面试我的用意,因为只有在了解了源码的工作原理后,你才能在面对疑难杂症,如何寻找解决问题的突破点。

handler工作流程

image.png

源码分析

1,sendMessage(msg:Message)**

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

    public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
         //当前时间加上延迟时间
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

    public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
        //mQueue 在 Looper构造方法中创建 并绑定
        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);
    }

跟踪sendMessage代码,发现最后都调用了sendMessageAtTime()方法,其实源码都为我们默认加了延迟时间,当我们没加延迟时间时系统默认为我们添加了0,就是不延迟。

2,enqueueMessage 消息队列

  private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
                                   long uptimeMillis) {
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();
        //mAsynchronous 为false new Handler()时源码为false
        // public Handler() {
        //        this(null, false);
        //    }
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

   boolean enqueueMessage(Message msg, long when) {
        //判断msg是否与Handler绑定
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        //判断msg是不是在使用
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }
        //消息队列加锁
        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;
            //判断消息队列里是否有消息,
            //如果是第一次添加消息,或者当前msg的时间小于mMessage的时间
            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.
                //不是第一次添加数据,并且msg的时间大于 mMessage(头指针)的时间
                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;
    }

跟踪代码到最后发现调用了native方法,无法再继续跟踪,根据方法字面意思大概是唤醒某个机制,上面这些代码始终没有handlerMessage,但是我们发现系统将我们的消息都插入到了一个链表MessageQueue中,在前面我们提到handler,MessageQueue,以及Looper的关系,所以我们联想到Looper一定在从MessageQueue轮询取消息。

3,Looper消息轮询
在前面提到的面试过程中,在子线程中如何创建handler问题,将在此处详细解释,

 Thread{
            val handler = Handler()
            handler.sendMessage(handler.obtainMessage())
        }.start()

当我们在使用上述这段代码处理问题时,结果程序挂了
image.png
报错信息提示无法创建hander,在我们没有调用Looper.prepare()的时候,所以解决这个问题就是我们要手动调用Looper.prepare()方法。

  Thread {
            Looper.prepare()
            val handler = Handler()
            handler.sendMessage(handler.obtainMessage())
            Looper.loop()
        }.start()

其实在上面的面试中,当我们回答玩这个问题的时候,面试官一定会接着往下问:为什么我们平时在主线程中使用handler时,并没有调用Looper.prepare(),却可以使用handler?其实在主线程中,不是不会报错,只是系统为我们调用了,进入ActivityThread中找到程序的入口函数:

public static void main(String[] args) {
    // ... 省略部分代码
    
    Looper.prepareMainLooper();

    ActivityThread thread = new ActivityThread();
    thread.attach(false);

    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }

    Looper.loop();

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


从上面代码中我们清楚的知道自己创建的时候调用了Looper.prepare(),ActivityThread创建的时候调用了prepareMainLooper(),如果我们弄清了这两个方法到底做了什么,应该问题就差不多了,我们先看ActivityThread中调用的prepareMainLooper()方法:

    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

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

    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

sThreadLocal.set(new Looper(quitAllowed));主要是为了保证一个线程只有一个Looper对象,其它代码都是相对简单的,Looper.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;

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        // Allow overriding a threshold with a system prop. e.g.
        // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
        final int thresholdOverride =
                SystemProperties.getInt("log.looper."
                        + Process.myUid() + "."
                        + Thread.currentThread().getName()
                        + ".slow", 0);

        boolean slowDeliveryDetected = false;
        //一个死循环 不停轮询消息
        for (;;) {
            //不断的从消息队列里面取消息
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }
            // Make sure the observer won't change while processing a transaction.
            final Observer observer = sObserver;

            final long traceTag = me.mTraceTag;
            long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
            long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
            if (thresholdOverride > 0) {
                slowDispatchThresholdMs = thresholdOverride;
                slowDeliveryThresholdMs = thresholdOverride;
            }
            final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
            final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);

            final boolean needStartTime = logSlowDelivery || logSlowDispatch;
            final boolean needEndTime = logSlowDispatch;

            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }

            final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
            final long dispatchEnd;
            Object token = null;
            if (observer != null) {
                token = observer.messageDispatchStarting();
            }
            long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
            try {
                // 通过 target 去 dispatchMessage 而 target 就是绑定的 Handler
                //创建enqueueMessage时绑定,前面代码有注释
                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);
                }
            }
            if (logSlowDelivery) {
                if (slowDeliveryDetected) {
                    if ((dispatchStart - msg.when) <= 10) {
                        Slog.w(TAG, "Drained");
                        slowDeliveryDetected = false;
                    }
                } else {
                    if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
                            msg)) {
                        // Once we write a slow delivery log, suppress until the queue drains.
                        slowDeliveryDetected = true;
                    }
                }
            }
            if (logSlowDispatch) {
                showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
            }

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }
            //消息回收循环利用,所以在我们发消息的时候尽量使用obtain()获取message对象
            //不要new Message()这样循环利用消息,就不会导致浪费资源
            msg.recycleUnchecked();
        }
    }

看到这里我们基本理清了Handler整个消息发送,存储,取出,处理过程,在整个Handler源码中,这些只是很浅显的一部分,比如它是如何线程同步的,消息同步屏障这些难点问题包括前面提到的obtain()消息,这里面就使用了享元模式的设计思想,包括整个handler消息处理其实是成产与消费的过程,,,等等,这些都值得我们一一去阅读源码分析,源码中有太多宝藏值得我们挖取,如果你觉得分析的不到位, 欢迎留言讨论。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值