Android线程通信-Handler

Android是基于事件驱动的,由一个一个的事件组成了App进程。当没有事件时会进行休眠等待,当事件来临时再次进行唤醒,所以Android进程的生命周期才会那么长久。而事件来临以后各个线程之间是如何进行通信与相应的呢?没有事件时Android又是如何进行休眠与唤醒的?这就要用到我们今天的主角-Handler
HandlerAndroid进程内的通信的阿一种机制。我们首先要了解Handler通信的原理是什么?进程间内存共享!所以Handler的本质就是多线程针对共享内存数据Message进行处理,而Message内存储了Handler的对象保证处理与发送的是同一个Handler。但是Handler如何做到跨线程的呢?阿多线程的处理同一个Message的话同步如何保证?这就要用到LooperMessageQueue了,LooperHandler绑定后又通过LocalThread机制保证只在当前创建线程有且只有一个,MessageQueue是一个单链表的队列,保证Message顺序执行。下面通过源码一步一步的解析一下Handler的神秘面纱。

首先我们要创建一个Hander的通信代码,这里用到HandlerThread在子线程创建,因为在主线程Android内部已经替我们实现了Looper

    //1.创建Looper与MessageQueue,执行Looper.loop开启循环,将Handler与Looper绑定
    val handlerThread=HandlerThread("test")
    handlerThread.start()
    val handler=Handler(handlerThread.looper){
        //5.Message事件处理
         true
    }
    //2.从Message池中取出一个Message对象
    val msg= Message.obtain()
    //3.Message与Handler绑定并发送Message到消息队列中
    handler.sendMessage(msg)
    handler.post {
        //TODO message runnable
    }
    //4.等待MessageQueue中取出Message并进行时间分发
1.创建Looper与MessageQueue,执行Looper.loop开启循环
 @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }

HadlerThreadAndorid为我们提供的一个子线程,在内部的run方法内为我们创建了Looper对象。首先执行prepare()方法创建Looper对象并与ThreadLocal绑定,然后执行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));
    }

prepare方法调用内部prepare(boolean quitAllowed)。首先调用sThreadLocal.get()查看当前线程是否存在Looper对象,如果存在的话抛出异常,不存在的话则创建新的Looper对象并与ThreadLocal绑定。这里的判断主要是为了保证一个线程内只能有一个Looper对象,而Looper对象创建的时候会同步创建MessageQueue队列,这样也就保证在只有在当前线程有且仅有一个可以处理的所有其余线程向消息队列发送的的Message对象,从而进行跨线程通信。同时这里也就解释了为什么一个线程可以有对个Handler确只能有一个Looper和一个MessageQueue。因为只有一个Looper是保证跨线程通信的关键,他负责在当前线程处理其余线程发送来的共享内存对象-Message

ThreadLocal
下面我们在看一下Handler是如何保证一个线程只有一个Looper对象的。---ThreadLocal

public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
 }

这是ThreadLocal内部的get方法,首先会获取当前线程并传入getMap方法中,那么在getMap中做了什么?

 ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

可以看到getMap是从Thread内获取了一个ThreadLocalMap对象,这是一个Map类型的数据,key值是ThreadLocal对象。

获取ThreadLocalMap后调用了 map.getEntry(this)去获取Looper对象。

看到这里我们大概了解了Looper到底是如何与Thread绑定的。首先Thread内部存储了一个ThreadLocalMap对象,然后在Map内存储Looper对象,而且key值为ThreadLocal对象。也就是Looper本质是存储在当前线程中的。但是如何保证一个线程只有一个Looper对象呢?这就要保证 map.getEntry(this)的这个key值固定了,这样每次去ThreadLocalMap中获取Looper对象时才能保证有且仅有一个。我们在看一下ThreadLocal的定义。

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

我们可以看见sThreadLocal是一个常量,而常量的定义就是固定不可变的。所以ThreadLocal固定也就保证了 map.getEntry(this)取到的Looper固定。从而也就保证了当前线程内的Looper与线程绑定且就每次获取的都是同一个。

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

MessageQueue
Looper构造函数内创建MessageQueue对象,保证了消息队列与Looper拥有同样的单一性。

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

Looperloop循环方法内部是一个for (;;) 的死循环。只有当msg == null时候才会退出,但是这种情况再正常循环过程中不会出现,因为queue.next()在没有数据时会进行阻塞,只有在退出Handler时才会返回null。在这个loop循环中起始主要做了俩件事:一是从queue中取message;二是将msg分发出去给Handler处理。那么我们去看一下queue.next()里面到底干了什么吧。

@UnsupportedAppUsage
Message next() {
    // NativeMessageQueue中的指针
    final long ptr = mPtr;
    if (ptr == 0) {
        return null;
    }
    //Handler空闲任务数量
    int pendingIdleHandlerCount = -1; 
    //阻塞时间 -1一直阻塞 0为不阻塞
    int nextPollTimeoutMillis = 0;
    for (;;) {
        //刷新执行Binder任务
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
        //native层执行阻塞
        nativePollOnce(ptr, nextPollTimeoutMillis);

        synchronized (this) {
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            //同步屏障
            if (msg != null && msg.target == null) {
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                if (now < msg.when) {
                    // 延时Message开启阻塞
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                   // 返回对应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 {
                // 没有消息 阻塞队列
                nextPollTimeoutMillis = -1;
            }

            // 退出Handler 返回null
            if (mQuitting) {
                dispose();
                return null;
            }

            //ideaHandler
            if (pendingIdleHandlerCount < 0
                    && (mMessages == null || now < mMessages.when)) {
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
            if (pendingIdleHandlerCount <= 0) {
                // 没有IdeaHander执行阻塞
                mBlocked = true;
                continue;
            }

            if (mPendingIdleHandlers == null) {
                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
            }
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }

        // 执行IdeaHandler
        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);
                }
            }
        }
        pendingIdleHandlerCount = 0;
        nextPollTimeoutMillis = 0;
    }
}

在queue.next()中,从上而下主要有以下几点需要重点关注:

1.队列的阻塞与唤醒

nativePollOnce是在native层去阻塞MessageQueue的循环,其中nativiPollince传入俩个参数:ptrnativeMessageQueue中的指针,nextPollTimeoutMillis为阻塞时间,0为不阻塞(第一次默认为0),-1为当队列没有消息或者插入新消息还未唤醒时会一直阻塞(当消息队列为空时传-1),直到调用nativeWake函数唤醒。还有就是正常的阻塞时间(当遇见延时Handler时传入对应的延时时间)。

nativeWake是唤醒MessageQueue使用的,当向队列中插入一条Message对象时,此时会根据当前队列状态判断是否需要去执行这个方法去唤醒队列。

nativieWakenativePollOnce都是将队列的阻塞与唤醒放到了native层,然后利用系统的的epoll机制将自己阻塞在消息队列的文件描述符可读事件上,直到有新消息被放入MessageQueue,导致文件描述符变为可读状态,或者等待超时。同时native层也持有了自己的Looper对象与MessageQueue队列,保证在native层也可以使用Handler通信机制。

2.同步屏障

好的,我们接着往下看,走完阻塞方法后,我们就拿到当前的message对象了,然后首先我们需要判断messagetarget对象是否为null同步屏障了(msg! = null && msg.target == null)。其中target对象为Message中携带的Handler对象(后面会讲)。当确定当前是一个同步屏障标志Message后,会进行while循环遍历当前队列中的异步消息优先处理(其中如果找到异步消息会拿出该消息去分发处理,同时不更改现在的mMessages对象从而确保同步屏障标志消息还在保证下一次仍是优先处理一步消息)。当时当队列中没有一步消息以后,此时队列中的第一个仍然是同步屏障消息,此时就会一直执行while直至msg=null然后执行return不就导致死循环了吗?所以如果我们自己写同步屏障的话一定要记着当不需要同步屏障时需要移除同步屏障

插入机制:同步屏障的插入通常是通过MessageQueue的特定方法来实现的。一般情况下,这个操作是在系统底层或者特定的框架代码中完成的。例如,在 Android 的视图系统中,当需要优先处理屏幕刷新相关的异步消息时,系统会在消息队列中插入同步屏障。插入过程可能涉及到设置消息的属性(如前面提到的设置targetnull和特定的标志位),并将这个特殊的消息插入到消息队列的合适位置(通常是头部或者特定位置,以影响消息处理顺序)。

移除机制:同样,同步屏障的移除也是通过MessageQueue的相关操作。在合适的时机,当不需要同步屏障来控制消息处理顺序时(例如,所有需要优先处理的异步消息都已经处理完毕),系统会从消息队列中移除同步屏障。这个过程可能需要遍历消息队列来找到同步屏障消息并将其删除,恢复消息队列的正常处理顺序。不过,在实际应用中,开发者一般很少直接操作同步屏障的插入和移除,这些操作大多是由系统自动完成的,以保证系统的稳定性和消息处理的高效性。

下面是ViewRootImpl中对同步屏障的使用,保证了UI刷新的及时性。

void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        // 发送屏障消息
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        // 在这个地方会有发送异步消息
        //异步消息创建方式
        //Message msg = Message.obtain();
        //msg.flags |= Message.FLAG_ASYNCHRONOUS;
        //或
        //msg.setAsynchronous(true);
        mChoreographer.postCallback(
            Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }
}

void doTraversal() {
    if (mTraversalScheduled) {
        mTraversalScheduled = false;
        // 移除屏障消息
        mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

        if (mProfile) {
            Debug.startMethodTracing("ViewAncestor");
        }

        performTraversals();

        if (mProfile) {
            Debug.stopMethodTracing();
            mProfile = false;
        }
    }
}

3.Handler的退出

从looper.loop()方法中我们曾经看到是一个死循环,只有当queue.next()返回的msg为null时才会退出loop循环。而next方法内我们可以看见只有一个地方返回null其余条件下都会阻塞队列。

if (mQuitting) {
     dispose();
     return null;
}

我们可以看见只有mQuitting这个标志位true时message才会返回null所以这个标志位是什么时候改变的就是Handler退出的关键,我们全局搜索一下关于这个标志位是如何更改的。

    void quit(boolean safe) {
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }

        synchronized (this) {
            if (mQuitting) {
                return;
            }
            mQuitting = true;

            if (safe) {
                removeAllFutureMessagesLocked();
            } else {
                removeAllMessagesLocked();
            }

            // We can assume mPtr != 0 because mQuitting was previously false.
            nativeWake(mPtr);
        }
    }

可以看到这就是Handler退出的代码了,其中传入了一个safe关键字。我们可以看见Handler中首先将标志位设置为了true。然后根据safe的值去决定执行removeAllFutureMessagesLockedremoveAllMessagesLocked方法去移除未完成的消息,最后调用nativeWake方法通知消息队列去执行退出。

我们再看一下这俩个清除消息的方法有什么不同?

    private void removeAllFutureMessagesLocked() {
        final long now = SystemClock.uptimeMillis();
        Message p = mMessages;
        if (p != null) {
            if (p.when > now) {
                removeAllMessagesLocked();
            } else {
                Message n;
                for (;;) {
                    n = p.next;
                    if (n == null) {
                        return;
                    }
                    if (n.when > now) {
                        break;
                    }
                    p = n;
                }
                p.next = null;
                do {
                    p = n;
                    n = p.next;
                    p.recycleUnchecked();
                } while (n != null);
            }
        }
    }
     private void removeAllMessagesLocked() {
        Message p = mMessages;
        while (p != null) {
            Message n = p.next;
            p.recycleUnchecked();
            p = n;
        }
        mMessages = null;
    }

我们知道Handler中的消息队列是用链表来实现的,所以当safe=false时候只是单纯的将链表中的数据都移除掉直至mMessages=null。而相反当安全的退出Handler时,我们可以看见当p.when > now时我们也是移除全部数据,而当消息的执行时间小于等于当前时间的时候,我们则不移除而是让他执行完成,然后移除掉后续的其他所有消息。

4.IdeaHandler

我们看代码时会发现当队列中没有消息后首先会将nextPollTimeoutMillis值设置成-1去阻塞队列,但是后面还有一串代码去执行,这些是什么呢?这就是IdeaHandler,当线程的MessageQueue空闲的时候(MessageQueue中没有Message或者下一次要执行的Message的时间还未到)就会去执行IdleHandler中指定的任务,它常用来做一些不是特别紧急的任务,比如资源的回收、延时加载等。

IdleHandlerMessageQueue中的一个接口。根据

  if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }

我们看到只有消息队列为空或者当前消息的执行时间>当前时间时,才会去查询IdleHandler的数量,而mIdleHandlers则是一个ArrayList用来存储需要闲时执行的任务。拿到这些任务后

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

会通过for循环一次遍历这些任务去执行任务。keep = idler.queueIdle()会将queueIdle()方法的返回值赋值给keep,如果queueIdle方法的返回值为false则执行之后会将此IdleHandler对象移除。如果返回true的话则不移除,下次调用next方法如果条件满足仍然会执行。

IdeaHandler的使用方式:

Looper.getMainLooper().getQueue().addIdleHandler(new MessageQueue.IdleHandler() {
    @Override
    public boolean queueIdle() {
        //返回true的话则此IdeaHandler不会被移除
        return false;
    }
});

ActivityThread中有一个GCIdleHandler,用于做GC的:

class H extends Handler {
    ......
    public static final int GC_WHEN_IDLE = 120;
    ......

    public void handleMessage(Message msg) {
        switch (msg.what) {
            ......
            case GC_WHEN_IDLE:
                scheduleGcIdler();
                break;
            ......
        }
    }
}

//当收到 GC_WHEN_IDLE 消息后,就会触发 scheduleGcIdler()
void scheduleGcIdler() {
    if (!mGcIdlerScheduled) {
        mGcIdlerScheduled = true;
        Looper.myQueue().addIdleHandler(mGcIdler);
    }
    mH.removeMessages(H.GC_WHEN_IDLE);
}

void unscheduleGcIdler() {
    if (mGcIdlerScheduled) {
        mGcIdlerScheduled = false;
        Looper.myQueue().removeIdleHandler(mGcIdler);
    }
    mH.removeMessages(H.GC_WHEN_IDLE);
}

final class GcIdler implements MessageQueue.IdleHandler {
    @Override
    public final boolean queueIdle() {
        doGcIfNeeded();
        purgePendingResources();
        return false;
    }
}

void doGcIfNeeded() {
    doGcIfNeeded("bg");
}

void doGcIfNeeded(String reason) {
    mGcIdlerScheduled = false;
    final long now = SystemClock.uptimeMillis();
    //Slog.i(TAG, "**** WE MIGHT WANT TO GC: then=" + Binder.getLastGcTime()
    //        + "m now=" + now);
    if ((BinderInternal.getLastGcTime()+MIN_TIME_BETWEEN_GCS) < now) {
        //Slog.i(TAG, "**** WE DO, WE DO WANT TO GC!");
        BinderInternal.forceGc(reason);
    }
}

ok到现在我们就了解了HandlerLooper与消息队列是如何创建的。loop循环何时执行如何阻塞、何时退出、的以及如何去取到message对象,还有就是空闲的IdeaHandler。到此为止Hander中接受消息的准备工作都已经完成了,现在我们就接着以message的视角去看一下它是如何创造,发送以及分发处理的。

Message的创建

Message的创建有俩种方式

 Message msg = new Message();
 Message msg = Message.obtain();

其中手动创建一个Message的方式不推荐,为什么不推荐的原因就在obtain上,我们看一下这个方法都做了什么。

    private static Message sPool;
    private static int sPoolSize = 0;   
    private static final int MAX_POOL_SIZE = 50;
    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }
    void recycleUnchecked() {
        // Mark the message as in use while it remains in the recycled object pool.
        // Clear out all other details.
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = UID_NONE;
        workSourceUid = UID_NONE;
        when = 0;
        target = null;
        callback = null;
        data = null;

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

可以看到obtain方法内部也实现了new Message()。但是他还为我们创建了一个消息池,当消息池为null时回去主动创建Message对象,不为空的话则直接用消息池中的Message对象。这样的好处的话是可以避免重复创建对象,减小内存负担。而当Message消息处理完以后最终就会执行recycleUnchecked方法对Message对象进行回收,重新放回消息池中等待下次使用。

Message的发送

我们先考虑一下几个问题。Message的延时消息是如何排序的?Message中如何保证处理自己的是哪个HandlerMessage是如何在队列中标志自己为异步消息的?这些都在Messgae的发送中可以找到答案。我们先从sendMessage开始看。

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

sendMessageSendMessageDalayed最后都会调用到senMessageAtTime这个函数中,在senMessageAtTime中我们获取了queue消息队列,获取方式的话是根据Handler绑定的Looper对象获取到的。最后又会调用到enqueueMessage方法中,不同支出只是针对delayMillis这个参数做了处理,这个就是延时的时间戳。

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

enqueueMessage中我们看见为Messagetarget赋值为this。而这个方法又在Handler内,所以这里便为Message传入了Handler对象,从而再此将HandlerMessage对象绑定。所以后续的Message分发处理中我们用到我们处理Message时也就用到了这个Handler。所以保证了Message对象谁创建谁处理。同时我们还看见了msg.setAsynchronous(true),这个方法主要是针对异步函数做的处理,为异步函数添加了一个标注为用于区分。最后调用消息队列的

现在我们看关键函数enqueueMessage

boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        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;
            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;
    }

在这里我们首先看到

if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
}

target为空不是同步屏障吗?插入以后为什么会直接抛异常?那是因为同步屏障不是在这个方法插入的,而是postSyncBarrier中,在这个方法会直接为我们创建Message对象并将target设置为空插入到消息队列中。

在插入消息时首先判断了mQuitting是否为true,如果已经开始退出了便不再接受新的消息直接返回false。如果可以插入的话便会根据消息的执行时间when在队列中对消息进行排序插入。最后根据当前队列状态决定是否需要调用nativeWake唤醒队列。从而完成了一次插入消息的操作。

消息的分发处理

我们刚才在loop方法中曾看到消息是如何拿到的,那么拿到消息以后就需要处理这条消息了。这里主要调用了一个方法。

msg.target.dispatchMessage(msg);

没错就是这个一行,调用了target也就是HandlerdispatchMessage方法。用这里我们也能证明Message是谁创建谁处理的属性。

那么我们看一下这个dispathMessage做了什么吧。

   public void dispatchMessage(@NonNull Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
    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);
    }

从代码看的话很好解释这个流程了,首先判断msgcallback是否为空。这个callback是在Message.post方法去创建的。

  private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

也就是如果是post的消息的话则由Message自己处理,如果是普通消息的话则调用Handlercallback回到。这个callback实在Handler初始化的时候由我们去实现的hadnleMessage方法。

至此Handler中消息时如何创建、如何入队列、如何读取、如何处理的我们都已经了解了。最后当消息处理完以后调用

 msg.recycleUnchecked();

对消息进行回收处理便完成了一次Handler通信。
注:Hnadler有一个方法Handler.runWithScissors可以保证当前Handler消息处理后再处理后续代码,他会在内部设置一个标记为done,只有在执行完run方法处理完消息后,done才会改为true继续执行发送Handler消息的后续代码,佛则会wait阻塞当前代码。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容