• Android 消息传递机制


    线程间消息传递机制

    http://androidxref.com/6.0.1_r10/xref/frameworks/base/core/java/android/os/Handler.java

    http://androidxref.com/6.0.1_r10/xref/frameworks/base/core/java/android/os/MessageQueue.java

    http://androidxref.com/6.0.1_r10/xref/frameworks/base/core/java/android/os/Looper.java

    http://androidxref.com/6.0.1_r10/xref/frameworks/base/core/java/android/os/Message.java

     1.消息怎么发送的? 

    我们都知道当调用Handler发送消息的时候,不管是调用

    sendMessage,sendEmptyMessage,sendMessageDelayed还是其他发送一系列方法。最终都会调用sendMessageDelayed(Message msg, long delayMillis)方法。

      public final boolean sendMessageDelayed(Message msg, long delayMillis)
        {
            if (delayMillis < 0) {
                delayMillis = 0;
            }
            return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
        }
    该方法会调用sendMessageAtTime()方法。其中第二个参数是执行消息的时间,是通过从开机到现在的毫秒数(手机睡眠的时间不包括在内)+ 延迟执行的时间。这里不使用System.currentTimeMillis() ,是因为该时间是可以修改的。会导致延迟消息等待时间不准确。该方法内部会调用sendMessageAtTime()方法,我们接着往下走。
       public boolean sendMessageAtTime(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);
        }
     
        private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
            msg.target = this;
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
            return queue.enqueueMessage(msg, uptimeMillis);
        }
    这里获取到线程中的MessageQueue对象mQueue(在构造函数通过Looper获得的),并调用enqueueMessage()方法,enqueueMessage()方法最终内部会调用MessageQueue的enqueueMessage()方法,那现在我们就直接看MessageQueue中把消息加入消息队列中的方法。

    消息的加入

    当通过handler调用一系列方法如sendMessage()、sendMessageDelayed()方法时,最终会调用MessageQueue的enqueueMessage()方法。现在我们就来看看,消息是怎么加入MessageQueue(消息队列)中去的。
    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 {
                    //判断唤醒条件,当前当前消息队列头部消息是屏障消息,且当前插入的消息为异步消息
                    //且当前消息队列处于无消息可处理的状态
                    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; 
                    prev.next = msg;
                }
    
                //调用nativeWake,以触发nativePollOnce函数结束等待
                if (needWake) {
                    nativeWake(mPtr);
                }
            }
            return true;
        }

    这里大家肯定注意到了nativeWake()方法,这里先不对该方法进行详细的描述,下文会对其解释及介绍。 其实通过代码大家就应该发现了,在将消息加入到消息队列中时,已经将消息按照等待时间进行了排序。排序分为两种情况(这里图中的message.when是与当前的系统的时间差):

    • 第一种:如果队列中没有消息,或者当前进入的消息比消息队列中头部的消息等待时间短,那么就放在消息队列的头部

     第二种:反之,循环遍历消息队列,把当前进入的消息放入合适的位置(比较等待时间)

     综上,我们了解了在我们使用Handler发送消息时,当消息进入到MessageQueue(消息队列)中时,已经按照等待时间进行了排序,且其头部对应的消息是Loop即将取出的消息

    android_os_MessageQueue_nativeWake
    static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
        NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
        nativeMessageQueue->wake();
    }

    和之前一样,也是通过long类型的ptr获取NativeMessageQueue对象的指针,再调用wake方法

    void NativeMessageQueue::wake() {
        mLooper->wake();
    }

    调用的也是Looper的方法:

    void Looper::wake() {
        uint64_t inc = 1;
        ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
        if (nWrite != sizeof(uint64_t)) {
            if (errno != EAGAIN) {
                ALOGW("Could not write wake signal: %s", strerror(errno));
            }
        }
    }

    重点是write(mWakeEventFd, &inc, sizeof(uint64_t)),写入了一个1,这个时候epoll就能监听到事件,也就被唤醒了

    Looper的wake方法其实是使用write函数通过mWakeEventFd往管道写入字符inc,其中TEMP_FAILURE_RETRY 是一个宏定义, 当执行write方法失败后,会不断重复执行,直到执行成功为止,在nativeinit中,我们已经通过epoll_create方法监听了mWakeEventFd的可读事件,当mWakeEventFd可读时,epoll文件描述符就会监听到,这时epoll_wait方法就会从管道中读取事件返回,返回后就执行消息处理逻辑,所以这里的往管道写入字符inc,其实起到一个通知的作用,告诉监听的线程有消息插入了消息队列了,快点醒过来(因为进入了休眠状态)处理一下。

     

    2.怎么样进行消息循环

    我们都知道消息的取出,是通过Loooper.loop()方法,其中loop()方法内部会调用MessageQueue中的next()方法。
      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) {
                  //...
                } finally {
                  //...
                }
                //...
    
                msg.recycleUnchecked();
            }
        }        

    从MessageQueue获取下一条消息

       1.如果是null,退出

       2.如果不为null,dispatchMessage

    怎么处理消费的分发过程?

    public void dispatchMessage(Message msg){
              //检查message的callback是否为null
              if(msg.calllback!=null){
                  handleCallback(msg);           
               }
                else{
                      if(mCallback!=null){
                                if(mCallback.handleMessage(mssg)){//主意如果这个返回值是true 将不会执行下面的handlerMessage
                                        return;
                                  } 
                       }
                        handleMessage(msg);
                 }
    }
    Handlet处理消息的过程
    首先,检查Message的callback是否为null,不为空就通过handleCallback来处理消息。message的callback是一个Runnbale对象,实际上就是Handler的post方法所传递的Runnable参数。
    private static void handleCallback(Message message){
            message.callback.run(); 
    }

    其次,检查mCallback是否为空,不为null及调用mCallback的handleMessage方法来处理消息

    public interface Callback{
          public boolean handleMessage(Message msg); 
    }

     MessageQueue的next方法

    Message next() {
            //...
            int nextPollTimeoutMillis = 0;
            for (;;) {
                if (nextPollTimeoutMillis != 0) {
                    Binder.flushPendingCommands();
                }
                
                //执行native层消息机制层,
                //timeOutMillis参数为超时等待时间。如果为-1,则表示无限等待,直到有事件发生为止。
                //如果值为0,则无需等待立即返回。该方法可能会阻塞
                nativePollOnce(ptr, nextPollTimeoutMillis);
    
                synchronized (this) {
                    //获取系统开机到现在的时间,如果使用System.currentMillis()会有误差,
                    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());
                    }
                      msg.next = null;
                            if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                            msg.markInUse();
                            return msg;
                    //...
            }
        }
    Message next() {
            
            //如果退出消息消息循环,那么就直接退出
            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();
                }
                
                //执行native层消息机制层,
                //timeOutMillis参数为超时等待时间。如果为-1,则表示无限等待,直到有事件发生为止。
                //如果值为0,则无需等待立即返回。该方法可能会阻塞
                nativePollOnce(ptr, nextPollTimeoutMillis);
    
                synchronized (this) {
                    //获取系统开机到现在的时间,如果使用System.currentMillis()会有误差,
                    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) {//判断当前消息时间,是不是比当前时间大,计算时间差
                            // 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 {
                            // 不需要等待时间或者等待时间已经到了,那么直接返回该消息
                            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;
                    }
    
                    // 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.
                    //获取空闲时处理任务的handler 用于发现线程何时阻塞等待更多消息的回调接口。
                    if (pendingIdleHandlerCount < 0
                            && (mMessages == null || now < mMessages.when)) {
                        pendingIdleHandlerCount = mIdleHandlers.size();
                    }
                    //如果空闲时处理任务的handler个数为0,继续让线程阻塞
                    if (pendingIdleHandlerCount <= 0) {
                        // No idle handlers to run.  Loop and wait some more.
                        mBlocked = true;
                        continue;
                    }
                    //判断当前空闲时处理任务的handler是否是为空
                    if (mPendingIdleHandlers == null) {
                        mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                    }
                    mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
                }
    
                //只有第一次迭代的时候,才会执行下面代码
                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);
                        }
                    }
                }
    
                // 重置空闲的handler个数,因为不需要重复执行
                pendingIdleHandlerCount = 0;
                
                //当执行完空闲的handler的时候,新的native消息可能会进入,所以唤醒Native消息机制层
                nextPollTimeoutMillis = 0;
            }
        }
    View Code
    MessageQueue的next()法肯定会很懵逼。妈的,这个nativePollOnce()方法是什么鬼,为毛它会阻塞呢?这个msg.isAsynchronous()判断又是怎么回事?妈的这个逻辑有点乱理解不了啊。大家不要慌,让我们带着这几个问题来慢慢分析

    Native消息机制

    其实在Android 消息处理机制中,不仅包括了Java层的消息机制处理,还包括了Native消息处理机制(与我们知道的Handler机制一样,也拥有Handler、Looper、MessageQueue)。这里我们不讲Native消息机制的具体代码细节,如果有兴趣的小伙伴,请查看----->深入理解Java Binder和MessageQueue

    这里我们用一张图来表示Native消息与Jave层消息的关系(这里为大家提供了Android源码,大家可以按需下载),具体细节如下图所示:




    static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
            jlong ptr, jint timeoutMillis) {
        NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
        nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
    }

     在nativePollOnce()方法中调用nativeMessageQueue的pollOnce()方法,我们接着走。

    void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
        mPollEnv = env;
        mPollObj = pollObj;
        mLooper->pollOnce(timeoutMillis);
        mPollObj = NULL;
        mPollEnv = NULL;
    
        if (mExceptionObj) {
            env->Throw(mExceptionObj);
            env->DeleteLocalRef(mExceptionObj);
            mExceptionObj = NULL;
        }
    }
    这里我们发现NativeMessageQueue的pollOnce(timeoutMillis)内部
    调用的是Native looper中的 pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData)方法。继续看
    int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
        int result = 0;
        for (;;) {
            // 先处理没有Callback方法的 Response事件
            while (mResponseIndex < mResponses.size()) {
                const Response& response = mResponses.itemAt(mResponseIndex++);
                int ident = response.request.ident;
                if (ident >= 0) { //ident大于0,则表示没有callback, 因为POLL_CALLBACK = -2,
                    int fd = response.request.fd;
                    int events = response.events;
                    void* data = response.request.data;
                    if (outFd != NULL) *outFd = fd;
                    if (outEvents != NULL) *outEvents = events;
                    if (outData != NULL) *outData = data;
                    return ident;
                }
            }
            if (result != 0) {
                if (outFd != NULL) *outFd = 0;
                if (outEvents != NULL) *outEvents = 0;
                if (outData != NULL) *outData = NULL;
                return result;
            }
            // 再处理内部轮询
            result = pollInner(timeoutMillis); 
        }
    }
    这里就简单介绍一下pollOnce()方法。该方法会一直等待Native消息,其中 timeOutMillis参数为超时等待时间。
    如果为-1,则表示无限等待,直到有事件发生为止。
    如果值为0,则无需等待立即返回。
    那么既然nativePollOnce()方法有可能阻塞,那么根据上文我们讨论的MessageQueue中的enqueueMessage中的nativeWake()方法。大家就应该了然了。nativeWake()方法就是唤醒Native消息机制不再等待消息而直接返回。

    nativePollOnce()一直循环为毛不造成主线程的卡死?

    到了这里,其实大家都会有个疑问,如果当前主线程的MessageQueue没有消息时,程序就会便阻塞在loop的queue.next()中的nativePollOnce()方法里,一直循环那么主线程为什么不卡死呢?这里就涉及到Linux pipe/epoll机制,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。 所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。

                          

  • 相关阅读:
    C# 文件流读写方法汇总
    C#中try catch finally的执行顺序(转载)
    Qt利用QPainter自绘实现热感应图效果
    Qt利用QPainter自绘实现扫描雷达功能scanneritem
    Qt5利用自绘QPainter实现旋转按钮MySpinButton
    Qt实现范围滑动条SuperSlider
    Qtt利用QPainter实现铵扭switchButton
    Qt5利用自绘QPainter实现水波纹进度条QProgressBarWater
    Qt5利用自绘实现云台GaugeCloud
    Qt5利用自绘实现遥感
  • 原文地址:https://www.cnblogs.com/mingfeng002/p/12084761.html
Copyright © 2020-2023  润新知