美文网首页
Android消息机制

Android消息机制

作者: wang_zd | 来源:发表于2021-01-03 21:42 被阅读0次

    一、概述

    Android的Handler消息机制涉及Android系统多个方面,例如ActivityService的生命周期调用,开发中展示从网络下载的数据,线程通信等。通过阅读源码解决下面问题。

    问题:

    • 消息处理的优先级?
    • 消息是怎么存储的?
    • 一个线程中LooperHandlerMessageQueue数量的对应关系?
    • 主线程Looper.loop()无限循环为什么不会ANR
    • 如何避免使用Handler造成的内存泄露?
    • IdleHandler是什么?有什么用?
    • 消息(Message)能没有target(Handler)吗?

    二、引出主题

    模拟使用Handler显示下载的数据。

           //step1,创建Handler
           var handler = object : Handler() {
                override fun dispatchMessage(msg: Message) {
                    println(msg.obj)
                }
            }
            //模拟线程下载数据
            Thread{
                Thread.sleep(1000)
                var msg=Message.obtain()
                msg.obj="download data"
                //step2,发送消息
                handler.sendMessage(msg)
            }.start()
    

    三、Handler

    1.发送消息

    使用无参构造创建的Handler会调用2个参数的构造方法。

        public Handler(@Nullable Callback callback, boolean async) {
            mLooper = Looper.myLooper();
            if (mLooper == null) {
                throw new RuntimeException(
                    "Can't create handler inside thread " + Thread.currentThread()
                            + " that has not called Looper.prepare()");
            }
            mQueue = mLooper.mQueue;
            mCallback = callback;
            mAsynchronous = async;
        }
    

    在这里会检查Looper是否初始化完成,但我们前面使用时并没有初始化这个Looper,为什么没有抛出这个RuntimeException?其实在App启动时系统已经帮我们初始化好了这个主线程的Looper。像每个Java程序都有个main方法的入口,App也有个main方法入口。在这个方法中,会初始化UI线程Looper,并开始无限循环,从这可以侧面看出整个应用的运转基本依赖于这套消息机制。

    ActivityThread.java
    public static void main(String[] args) {
           ...代码省略
           //初始化Looper
           Looper.prepareMainLooper();
           ActivityThread thread = new ActivityThread();
           thread.attach(false, startSeq);
    
           if (sMainThreadHandler == null) {
               sMainThreadHandler = thread.getHandler();
           }
           //开启循环
           Looper.loop();
           //执行到这就是出大问题了
           throw new RuntimeException("Main thread loop unexpectedly exited");
    }
    

    sendMessage()会一直调用到enqueueMessage(),然后指定msgtarget为当前Handler,在调用MessageQueueenqueueMessage()

    2.处理消息

    Looperloop方法中会不断取出消息,然后找到msgtarget处理消息,然后回调到HandlerdispatchMessage方法,通过源码可以看出,消息的处理优先级为:

    1. msgcallback优先级最高
    2. 然后是在创建Handler时指定的CallBack
    3. 最后才是HandlerhandleMessage方法
        public void dispatchMessage(@NonNull Message msg) {
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                handleMessage(msg);
            }
        }
    

    四、Looper

    1.prepare方法

    prepare方法会初始化Looper,保存在ThreadLocal中。ThreadLocal是一个与线程相关的数据保存类,保证了每个线程的这份数据独立性。所以通过sThreadLocal取出的Looper都是当前线程的独一份的Looper,得到的MessageQueue也是独一份。

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

    2.loop方法

    loop方法无限循环从MessageQueue中取出msg,交给Handler处理,然后将处理的msg放回消息池。如果消息队列为空了则结束此循环。

        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);
                } catch (Exception exception) {
                }
                msg.recycleUnchecked();
            }
         }
    

    五、MessageQueue

    1.添加消息enqueueMessage()

    MessageQueue意为消息队列,就是消息池。通过下面的enqueueMessage方法可以看出MessageQueue的实现方式是单链表。单链表在这的优势在于插入删除快,不用申请一块大的连续内存空间(相比数组)。

        boolean enqueueMessage(Message msg, long when) {
            //这种方式添加的msg必须有target
            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) {
                    //被标记退出循环,回收msg
                    msg.recycle();
                    return false;
                }
    
                msg.markInUse();
                msg.when = when;
                Message p = mMessages;
                boolean needWake;
                if (p == null || when == 0 || when < p.when) {
                    // 新头部,消息池中没消息,或者消息的处理时间是最早的。
                    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;
        }
    

    2.next方法

    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();
                }
               //nataive方法,阻塞方法。
                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) {
                        //最近的消息还没到触发时间,算出需要等好久,去nativePollOnce方法里等着
                        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;//从链表断开
                            msg.markInUse();
                            return msg;
                        }
                    } else {
                        // No more messages.
                        //没有消息去nativePollOnce一直等。
                        nextPollTimeoutMillis = -1;
                    }
    
                    // Process the quit message now that all pending messages have been handled.
                    if (mQuitting) {//标记退出循环
                        dispose();
                        return null;
                    }
                    //第一次空闲,得到IdleHandlers的数量
                    //队列为空或者第一条消息将来要处理时,才运行空闲处理。
                    if (pendingIdleHandlerCount < 0
                            && (mMessages == null || now < mMessages.when)) {
                        pendingIdleHandlerCount = mIdleHandlers.size();
                    }
                    if (pendingIdleHandlerCount <= 0) {
                        //没有idle handlers需要执行,循环去等着
                        mBlocked = true;
                        continue;
                    }
    
                    if (mPendingIdleHandlers == null) {
                        mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                    }
                    mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
                }
                // 运行idle handlers.
                // 只有第一次循环才会到这执行
                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 {//执行idler
                        keep = idler.queueIdle();
                    } catch (Throwable t) {
                        Log.wtf(TAG, "IdleHandler threw exception", t);
                    }
    
                    if (!keep) {//false执行一边就删除,true执行多次
                        synchronized (this) {
                            mIdleHandlers.remove(idler);
                        }
                    }
                }
    
                // 重置为0,我们就不会在再执行idle handler
                pendingIdleHandlerCount = 0;
    
                // 当我们执行了idle handler,可能就有新消息来了,就不等了,去看看。
                nextPollTimeoutMillis = 0;
    
    }
    

    nativePollOnce方法是一个native方法,没有消息处理时,程序会在这阻塞。nextPollTimeoutMillis表示阻塞的时间,-1表示一直会阻塞,0表示不阻塞,大于0表示下条消息等的时间。当没有消息时,会进入nativePollOnce方法里,线程会释放CPU的资源,进入休眠状态。

    如果没有消息处理,在进入等待前,会认为这个线程处于空闲状态,会把添加了的IdleHandler执行了。

    六、Message

    Message比较重要的是obtain()recycleUnchecked(),创建Message最好使用obtain()而不是直接newobtain()方法会从消息池中拿出一个已经创建好的Message,可以减少对象的创建。recycleUnchecked()则是将已经使用过的消息重新放回消息池,消息池最大为50。

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

    七、其它

    1.IdleHandler

    从上面分析MessageQueuenext()方法可以看出,当暂时没消息处理进入休眠前,它会查看是否有其它杂事要干,可以通过Looper.myQueue().addIdleHandler(MyIdleHandler())添加执行内容。所以IdleHandlerHandler提供的一种当消息队列空闲时,执行任务的机制。由于执行时机不稳定,可以用来处理一些不重要的杂事。例如ActivityThread中的GcIdler用来执行GC任务。注意返回falsetrue的区别,false执行一次,true执行多次。

    2.postSyncBarrier()

    postSyncBarrier()的作用是设置一个同步屏障。设置一个msg为屏障,它的标志是没有target,把这个msg按时间when插入链表中。在next()方法中,如果发现了同步屏障,则在链表中寻找第一个异步消息返回。这个机制就实现了,只要设置一个没有target的同步屏障msg,则消息机制就转成优先处理异步消息,同步消息就会阻塞到等同步屏障移除后才能执行。而postSyncBarrier()被标记为hide,普通开发者不能调用。它的作用在与处理一些优先级比较高的任务。比如绘制UI,在ViewRootImplscheduleTraversals()方法设置了同步屏障,保证了UI绘制优先执行。

        public int postSyncBarrier() {
            return postSyncBarrier(SystemClock.uptimeMillis());
        }
        private int postSyncBarrier(long when) {
            synchronized (this) {
                final int token = mNextBarrierToken++;
                final Message msg = Message.obtain();
                msg.markInUse();
                msg.when = when;
                msg.arg1 = token;
    
                Message prev = null;
                Message p = mMessages;
                if (when != 0) {
                    while (p != null && p.when <= when) {
                        prev = p;
                        p = p.next;
                    }
                }
                if (prev != null) { // invariant: p == prev.next
                    msg.next = p;
                    prev.next = msg;
                } else {
                    msg.next = p;
                    mMessages = msg;
                }
                return token;
            }
        }
        Message next() {
         ...
         synchronized (this) {
                    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());
                    }
          ...
        }
        ViewRootImpl.java
        void scheduleTraversals() {
            if (!mTraversalScheduled) {
                mTraversalScheduled = true;
               //设置同步屏障
                mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
                //发送异步消息
                mChoreographer.postCallback(
                        Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
                if (!mUnbufferedInputDispatch) {
                    scheduleConsumeBatchedInput();
                }
                notifyRendererOfFramePending();
                pokeDrawLockIfNeeded();
            }
        }
    

    八、总结

    • 消息处理的优先级?
    • 消息是怎么存储的?
    • 一个线程中LooperHandlerMessageQueue数量的对应关系?
    • 主线程Looper.loop()无限循环为什么不会ANR
    • 如何避免使用Handler造成的内存泄露?
    • IdleHandler是什么?有什么用?
    • 消息(Message)能没有target(Handler)吗?

    综上,问题基本能够解答,还差个内存泄露问题。造成这个问题的原因是,平时开发中创建Handler大多是在Activity中用非静态内部类或者匿名内部类的方式,Handler持有Activity的引用,当Activity被销毁时,如果还有消息msg没处理,而msg持有Handler引用,导致Activity不能被回收,造成内存泄露。

    解决办法是2种(打破这个引用链即可):

    1. 静态内部类+弱引用
    2. Activity销毁时调用handler.removeCallbacksAndMessages(null)清空没有处理完的消息。

    本文参考:
    Android消息机制1-Handler(Java层)
    面试官:“看你简历上写熟悉 Handler 机制,那聊聊 IdleHandler 吧?
    Android中为什么主线程不会因为Looper.loop()里的死循环卡死?
    揭秘 Android 消息机制之同步屏障:target==null ?
    Android 多线程:你的 Handler 内存泄露 了吗?

    相关文章

      网友评论

          本文标题:Android消息机制

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