美文网首页Basement Knowledgeandroid开发杂识Handler
Android消息机制(二):Message和MessageQu

Android消息机制(二):Message和MessageQu

作者: 张利强 | 来源:发表于2016-04-19 15:34 被阅读1270次

    Message

    消息结构

    每个消息用Message表示,Message主要包含以下内容:

    filed 含义 说明
    what 消息类别 由用户定义,用来区分不同的消息
    arg1 参数1 是一种轻量级的传递数据的方式
    arg2 参数2 是一种轻量级的传递数据的方式
    obj 消息内容 任意对象,但是使用Messenger跨进程传递Message时不能为null
    data Bundle数据 比较复杂的数据建议使用该变量(相比上面几个,这个县的比较重量级)
    target 消息响应方 关联的Handler对象,处理Message时会调用它分发处理Message对象
    when 触发响应时间 处理消息时间
    next Message队列里的下一个Message对象 用next指向下一条Message,实现一个链表数据结构,用户一般使用不到该字段。

    这里的用户指一般的APP开发者。

    一般不用手动设置target,调用Handler.obtainMessage()方法会自动的设置Message的target为当前的Handler。
    得到Message之后可以调用sendToTarget(),发送消息给Handler,Handler再把消息放到message queue的尾部。
    对Message除了给部分成员变量赋值外的操作都可以交由Handler来处理。

    消息池

    在通过Handler发送消息时,我们可以通过代码Message message=new Message();新建一条消息,但是我们并不推荐这样做,因为这样每次都会新建一条消息,很容易造成资源浪费。Android中设计了消息池用于避免该现象:

    • 获取消息 obtain()
      从消息池中获取消息: Message msg=Message.obtain();
      obtain()方法源码:
      public static Message obtain() {
            synchronized (sPoolSync) {
                if (sPool != null) {
                    Message m = sPool;
                    sPool = m.next;
                    m.next = null; //从sPool中取出一个Message对象,并消息链表断开
                    m.flags = 0; // clear in-use flag清除in-use flag
                    sPoolSize--;//消息池的可用大小进行-1操作
                    return m;
                }
            }
            return new Message();// 当消息池为空时,直接创建Message对象
        }
    

    从消息池取Message,都是把消息池表头的Message取走,再把表头指向下一条消息next;

    • 回收消息 recycle()
      把不再使用的消息回收到消息池 mgs.recycle();
      recycle()方法源码:
    public void recycle() {
            if (isInUse()) {//判断消息是否正在使用
                if (gCheckRecycle) {
                    throw new IllegalStateException("This message cannot be recycled because it "
                            + "is still in use.");
                }
                return;
            }
            recycleUnchecked();
        }
    
        /**
         * 对于不再使用的消息,加入到消息池
         * Recycles a Message that may be in-use.
         * Used internally by the MessageQueue and Looper when disposing of queued Messages.
         */
        void recycleUnchecked() {
            // Mark the message as in use while it remains in the recycled object pool.
            // Clear out all other details.
            //将消息标示位置为IN_USE,并清空消息所有的参数。
            flags = FLAG_IN_USE;
            what = 0;
            arg1 = 0;
            arg2 = 0;
            obj = null;
            replyTo = null;
            sendingUid = -1;
            when = 0;
            target = null;
            callback = null;
            data = null;
    
            synchronized (sPoolSync) {
                if (sPoolSize < MAX_POOL_SIZE) {//当消息池没有满时,将Message对象加入消息池
                    next = sPool;
                    sPool = this;
                    sPoolSize++;//消息池的可用大小进行加1操作
                }
            }
        }
    

    消息回收,就是将Message内容重置后,再把Message加到链表的表头,加入到消息池的过程;

    MessageQueue

    负责管理消息队列,实际上Message类有一个next字段,会将Message对象串在一起成为一个消息队列,所以并不需要LinkedList之类的数据结构将Message对象组在一起成为队列。

    • 创建消息队列
        MessageQueue(boolean quitAllowed) {
           mQuitAllowed = quitAllowed;
           mPtr = nativeInit();//通过native方法初始化消息队列,其中mPtr是供native代码使用
       }
    

    在MessageQueue初始化的时候调用了nativeInit,这是一个Native方法:

    static void android_os_MessageQueue_nativeInit(JNIEnv* env, jobject obj) { 
       NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue(); 
       if (!nativeMessageQueue) { 
           jniThrowRuntimeException(env, "Unable to allocate native queue"); 
           return;
           }
       nativeMessageQueue->incStrong(env); 
       android_os_MessageQueue_setNativeMessageQueue(env, obj, nativeMessageQueue);
    } 
    static void android_os_MessageQueue_setNativeMessageQueue(JNIEnv* env, jobject messageQueueObj,
           NativeMessageQueue* nativeMessageQueue) { 
       env->SetIntField(messageQueueObj, gMessageQueueClassInfo.mPtr, 
       reinterpret_cast<jint>(nativeMessageQueue)); 
     }
    

    方法名由java层类的包名+类名+方法名组成,这不是标准,是习惯写法,也可以采用其他名称组合,具体是什么名称由JNINativeMethod方法中Java对象与c++对象的映射决定,此处是JNI方面的内容,不作过多解释。
    在nativeInit中,new了一个Native层的MessageQueue的对象,并将其地址保存在了Java层MessageQueue的成员mPtr中,Android中有好多这样的实现,一个类在Java层与Native层都有实现,通过JNI的GetFieldID与SetIntField把Native层的类的实例地址保存到Java层类的实例的mPtr成员中,比如Parcel。

    再看NativeMessageQueue的实现:

    NativeMessageQueue::NativeMessageQueue() : mInCallback(false), mExceptionObj(NULL) { 
        mLooper = Looper::getForThread();
        if (mLooper == NULL) {
          mLooper = new Looper(false);
          Looper::setForThread(mLooper); 
        }
    }
    
    • 消息入队 enqueueMessage()
      enqueueMessage 用于将Message对象插入消息队列。MessageQueue永远是按照Message触发的时间先后顺序排列的,队头的消息是将要最早触发的消息。当有消息需要加入消息队列时,会从队列头开始遍历,直到找到消息应该插入的合适位置,以保证所有消息的时间顺序。
      该方法会被Handler对象调用。
      源码如下:
       /**
         * 添加一条消息到消息队列
         * @param msg 要添加的消息
         * @param when 消息处理时间
         * @return 添加成功与否
         */
        boolean enqueueMessage(Message msg, long when) {
            if (msg.target == null) {// 每一个Message必须有一个target
                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.
                    //p为null(代表MessageQueue没有消息) 或者msg的触发时间是队列中最早的, 则进入该该分支
                    msg.next = p;
                    mMessages = msg;
                    needWake = mBlocked;
                } else {
                    //将消息按时间顺序插入到MessageQueue。一般地,不需要唤醒事件队列,除非
                    //消息队头存在barrier,并且同时Message是队列中最早的异步消息。
    
                    // 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.
                //消息没有退出,我们认为此时mPtr != 0
                if (needWake) {
                    nativeWake(mPtr);
                }
            }
            return true;
        }
    
    • 消息轮询 next()
      最重要的方法,用于获取下一个Message对象,如果没有需要处理的Message对象,该方法将阻塞。MessageQueue用本地方法做同步互斥,因为这样时间更精准。每个Message对象都有一个什么时刻处理该Message对象的属性when,没到时间都不会处理该Message对象,如果时间不精准的话,会导致系统消息不能及时处理。
       /**
         * 依次从MessageQueue中取出Message
         * @return 消息
         */
        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  循环迭代的首次为-1
            int nextPollTimeoutMillis = 0;
            for (;;) {
                if (nextPollTimeoutMillis != 0) {
                    Binder.flushPendingCommands();
                }
                //阻塞操作,当等待nextPollTimeoutMillis时长,或者消息队列被唤醒,都会返回。
                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) {
                        //查询MessageQueue中的下一条异步消息
                        // 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();//设置消息flag成使用状态
                            return msg;//成功地获取MessageQueue中的下一条即将要执行的消息
                        }
                    } else {
                        // No more messages.//没有消息了
                        nextPollTimeoutMillis = -1;
                    }
    
                    // Process the quit message now that all pending messages have been handled.
                    if (mQuitting) {//消息正在退出,返回null
                        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) {
                        //没有idle handlers 需要运行,则循环并等待。
                        // 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.
                //只有第一次循环时,会运行idle handlers,执行完成后,重置pendingIdleHandlerCount为0.
                for (int i = 0; i < pendingIdleHandlerCount; i++) {
                    final IdleHandler idler = mPendingIdleHandlers[i];
                    mPendingIdleHandlers[i] = null; // release the reference to the handler//去掉handler的引用
    
                    boolean keep = false;
                    try {
                        keep = idler.queueIdle();//idle时执行的方法
                    } 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.
                //重置idle handler个数为0,以保证不会再次重复运行
                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.
                //当调用一个空闲handler时,一个新message能够被分发,因此无需等待可以直接查询pending message.
                nextPollTimeoutMillis = 0;
            }
        }
    

    nativePollOnce(ptr, nextPollTimeoutMillis)是一个native方法,是一个阻塞操作。其中nextPollTimeoutMillis代表下一个消息到来前,还需要等待的时长;当nextPollTimeoutMillis = -1时,表示消息队列中无消息,会一直等待下去。空闲后,往往会执行IdleHandler中的方法。当nativePollOnce()返回后,next()从mMessages中提取一个消息。nativePollOnce()在native做了大量的工作,想深入研究可查看资料: Android消息机制2-Handler(Native层)

    • 移除消息 removeMessages()
      就是将消息从链表移除,同时将移除的消息添加到消息池,提供循环复用。
      采用了两个while循环,第一个循环是从队头开始,移除符合条件的消息,第二个循环是从头部移除完连续的满足条件的消息之后,再从队列后面继续查询是否有满足条件的消息需要被移除。
        void removeMessages(Handler h, int what, Object object) {
            if (h == null) {
                return;
            }
    
            synchronized (this) {
                Message p = mMessages;
                //从消息队列的头部开始,移除所有符合条件的消息
                // Remove all messages at front.
                while (p != null && p.target == h && p.what == what
                       && (object == null || p.obj == object)) {
                    Message n = p.next;
                    mMessages = n;
                    p.recycleUnchecked();
                    p = n;
                }
    
                // Remove all messages after front.
                //移除剩余的符合要求的消息
                while (p != null) {
                    Message n = p.next;
                    if (n != null) {
                        if (n.target == h && n.what == what
                            && (object == null || n.obj == object)) {
                            Message nn = n.next;
                            n.recycleUnchecked();
                            p.next = nn;
                            continue;
                        }
                    }
                    p = n;
                }
            }
        }
    
    • 退出消息队列
      消息退出的方式:

      • 当safe =true时,只移除尚未触发的所有消息,对于正在触发的消息并不移除;
      • 当safe =flase时,移除所有的消息

    void quit(boolean safe) {
    if (!mQuitAllowed) {// 当mQuitAllowed为false,表示不运行退出,强行调用quit()会抛出异常
    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.
            //mQuitting=false,那么认定为 mPtr != 0
            nativeWake(mPtr);
        }
    }
    

    相关文章

      网友评论

      本文标题:Android消息机制(二):Message和MessageQu

      本文链接:https://www.haomeiwen.com/subject/cbfzlttx.html