美文网首页
Handler Message meesageQueue Loo

Handler Message meesageQueue Loo

作者: ironman_ | 来源:发表于2017-08-03 22:24 被阅读0次

    先来看一下Message从发出到处理的过程。

    先看一下Message的结构

    Message中的变量:

    //变量
    public int what;
    public int arg1;
    public int arg2;
    public Object obj;
    Bundle data;
    Handler target;//处理这个message的handler
    Runnable callback;//我们post的Runnable
    Message next;//他有next,相当于一个链表
    
    //静态变量,也就是相当于全局变量
    private static Message sPool;//已经用完的message,作为message池用来复用。(message.obtain()方法取的就是池子里的)最大长度50.
    private static int sPoolSize = 0;//当前池子的大小。
    

    再来看一下把Message放到MessageQueue中的地方:

        boolean enqueueMessage(Message msg, long when) {
    //省略一些代码
            synchronized (this) {
    //省略一些代码
                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;
        }
    

    代码比较长,我省略了一些不影响主流程的代码

    1. 将message标记为正在使用
    2. 给message的when赋值,也就是我们的是否postdelay,是delay则在当前时间加上delay时间否则when就是当前时间
    3. 根据when的值确定是将message添加到队列头还是添加到队列中间,如果是添加到队列头部则还会涉及到是否需要唤醒线程去处理消息。如果是插入队列中间则不需要去唤醒。
      (是否唤醒这块没看懂,涉及到native的方法。)

    再来看一下调用方looper

    looper会不断从messageQueue中获取消息处理
    loop方法中比较重要的几句

        public static void loop() {
            //...
            for (;;) {
                Message msg = queue.next(); // might block
              //...
                msg.target.dispatchMessage(msg);//调用handler的dispatchMessage处理消息
              //...
                msg.recycleUnchecked();//将msg加入到复用msg队列
            }
        }
    

    从上面的代码中可以看出

    • 他会调用MessageQueue的next方法获取Message
    • 获取到Message之后会通过handler的dispatchMessage方法来处理消息
    • 消息处理完成之后会回收消息。

    再看看MessageQueue的next方法。

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

    这个方法比较长,也比较难读懂,而且也有会调用C++的方法
    其实就是遍历Message队列,找到when不小于当前时间的Message返回,如果队列为空,或者Message的when都是未来时间,这时候会处理IdleHandler的东西。

    再来看handler的dispatchMessage方法:

        /**
         * Handle system messages here.
         */
        public void dispatchMessage(Message msg) {
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                handleMessage(msg);
            }
        }
    

    梳理一遍:

    一个Message从发出到处理需要经过的过程。

    1. post的不管是一个Message还是一个Runnable都会封装成一个Message。
    2. 被handler加入到messageQueue中,会根据Message的when来排序,也就是根据时间排序。
    3. Looper会不断从messageQueue中通过调用next获取message,这个next里有很多逻辑,涉及到idleHandler。
    4. Looper获取到Message之后会调用Message的target其实就是他引用的handler的dispatchMessage方法处理Message。
    5. 如果message有callback也就是Message本身是一个runnable的话就会执行这个runnable。
    6. 如果不是一个runnable,会先去调用handler的成员变量mCallback的handleMessage方法,如果这个的callback返回一个true则直接结束了,如果返回false会继续调用handler本身的handleMessage方法。

    到此这个message就被处理了。但是其实中间还有很多细节,比如
    在加入到messageQueue的时候,会通过message的when时间来插入队列,并且确定要不要唤醒队列(native方法,这里没太搞明白)

    一些细节:

    • Message中是含有handler的引用的,所以Handler的handlerMessage中如果引用了外部activity的非static的方法或者变量,就可能产生泄露,

    • 在sendMessage的时候他会把handler赋值给message的target

        private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
            msg.target = this;
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
            return queue.enqueueMessage(msg, uptimeMillis);
        }
    
    • postRunnable其实也是发送一个message:
        private static Message getPostMessage(Runnable r) {
            Message m = Message.obtain();
            m.callback = r;
            return m;
        }
    

    这个message有一个callback,

    • Message的复用机制
      Message使用完成之后会被标记为使用加入到一个static的链表的表头,下次通过Message的obtainMessage的时候就会从这里取而不是重新创建并且清除使用的flag标记,这个链表的最大长度是50.

    removeMessage如何操作的

    也就是通过what来removeMessage

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

    第一个while循环直接找当前节点以及当前节点的下一个节点是不是对应handler的以及what对不对(循环条件就是这个判断条件),如果中间不断开则一直执行,如果有一个节点不属于这个就会走到下一个循环,这时候的while循环的条件不再是判断条件,而是p等不等于null,也就是到没到链表结尾,中间的if做了判断,如果有节点满足条件则会被回收。

    removeCallback如何操作的

    跟上面的通过what来removeMessages差不多,不过这里是通过runnable来remove的

        void removeMessages(Handler h, Runnable r, Object object) {
            if (h == null || r == null) {
                return;
            }
    
            synchronized (this) {
                Message p = mMessages;
    
                // Remove all messages at front.
                while (p != null && p.target == h && p.callback == r
                       && (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.callback == r
                            && (object == null || n.obj == object)) {
                            Message nn = n.next;
                            n.recycleUnchecked();
                            p.next = nn;
                            continue;
                        }
                    }
                    p = n;
                }
            }
        }
    

    从上面两个方法看来removeCallbacks等等都需要具体的runnable或者what参数,所以这两个方法直接传null并不会执行任何东西。

    如何remove这个Handler发出的所有Message

    如果想要移除对应handler所有的message可以使用这个:

        /**
         * Remove any pending posts of callbacks and sent messages whose
         * <var>obj</var> is <var>token</var>.  If <var>token</var> is null,
         * all callbacks and messages will be removed.
         */
        public final void removeCallbacksAndMessages(Object token) {
            mQueue.removeCallbacksAndMessages(this, token);
        }
    

    object传一个null。
    因为这个代码是这么写的:

        void removeCallbacksAndMessages(Handler h, Object object) {
            if (h == null) {
                return;
            }
    
            synchronized (this) {
                Message p = mMessages;
    
                // Remove all messages at front.
                while (p != null && p.target == h
                        && (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 && (object == null || n.obj == object)) {
                            Message nn = n.next;
                            n.recycleUnchecked();
                            p.next = nn;
                            continue;
                        }
                    }
                    p = n;
                }
            }
        }
    

    如果object传null会移除这个handler的所有的message。

    所以总结一下怎么使用handler会产生泄露,如何避免

    先了解一下什么是内存泄露,什么时候会内存泄露:


    如上图,当一个生命周期长的对象B引用了一个什么周期短的对象A,这个时候生命周期短的对象A就泄露了,他本来早就应该被回收的。比如Message可能长时间存活于MessageQueue中,而Message引用了Runnable,Runnable隐式引用了activity,这时候activity的对象的生命周期就被迫和Message的生命周期一样长了,如果一个MessagePostDelay10分钟,如果用户反复打开这个activity,每次打开创建一个实例,该销毁的销毁不了这时候就会出现很多这个activity的实例,这时候这个activity就泄露了。

    在看看具体的使用场景

    使用handler有两种方式:

    • 一种postRunnable,在Runnable里做更新操作。
    • 一种postMessage,在handleCallback里做操作(不管是callback的handleMessage还是handler本身的handleMessage)

    使用Runnable,如果是在activity中使用Runnable,Runnable是一个非static的内部类的时候,他是会持有外部类的引用的,所以runnable引用activity,message引用runnable,而message的生命周期又超长,activity就会被泄露。
    这时候可以在activity的onDestroy里使用handler的handler.removeCallbacksAndMessages(null)这个方法会移除这个handler所发出的所有的mssage(runnable也是message),(当然这里也可以通过removeMessage传入一个Runnable来移除消息,但是你需要一直引用你的那个Runnable)

    使用Message,然后通过message.what在handleMessage中处理。
    这里会涉及到两种方式:
    在创建handler的时候传入了一个callback,在这个callback的handleMessage里处理消息。
    还有就是直接复写handler的handleMessage方法。
    这两种在dispatch的时候还是有区别的:

        /**
         * Handle system messages here.
         */
        public void dispatchMessage(Message msg) {
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                handleMessage(msg);
            }
        }
    

    会先处理callback的handleMessage,如果这里返回了true,则不会再去调用handler的handleMessage,否则会调用handler的handleMessage方法。

    这两种方法的泄露点在哪里呢?

    Message是引用handler的,handler如果不是static的那他就会持有一个外部对象的引用,很多时候他的外部对象都是activity,所以也就是长生命周期的Message间接引用了activity。

    避免这两种用法带来的内存泄露有两种方法:

    通过what来removeMessage
    还是通过handler.removeCallbacksAndMessages(null)来移除这个handler发送的所有的消息。

    相关文章

      网友评论

          本文标题:Handler Message meesageQueue Loo

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