美文网首页
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

    先来看一下Message从发出到处理的过程。 先看一下Message的结构 Message中的变量: 再来看一下把...

  • Android @ 消息机制

    Looper、Handler、MessageQueue、Message参考: Android 消息处理机制(Loo...

  • Handler源码阅读

    1. Handler构造方法 2. 给Handler发送一个消息 该Message将会保存在Handler的Loo...

  • Android的Handler消息处理机制

    实现android的消息机制在应用层会设置 Handler, Message ,MessageQueue ,Loo...

  • handler+Message+MessageQueue+Loo

    1.handler消息处理器,负责处理消息 2.Message消息,包含消息id、被处理的对象 3.Message...

  • 异步消息处理机制

    Android中的异步消息处理主要由4个部分组成:Message、Handler、MessageQueue和Loo...

  • 消息处理

    Android中的异步消息处理主要由四个部分组成,Message、Handler、MessageQueue和Loo...

  • Handler机制之Message

    其实是想跟着书看的,自己先大概浏览了一下Handler的源码,关于Message、MessageQueue、Loo...

  • Handler机制

    handler机制 handler Message MessageQueue Looper 1. Message...

  • Handler和Message

    1. handler和message 2.message 2.handler

网友评论

      本文标题:Handler Message meesageQueue Loo

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