Android 消息机制

作者: pj0579 | 来源:发表于2019-09-25 17:04 被阅读0次

    首先明确这一点: 交互基于消息机制
    消息机制主要包含:MessageQueueMessage,HandlerLooper这四大部分
    拿一个典型例子分析消息机制怎么工作的

    public class Activity extends android.app.Activity {
        private Handler mHandler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                System.out.println(msg.what);
            }
        };
        @Override
        public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
            super.onCreate(savedInstanceState, persistentState);
            setContentView(R.layout.activity_main);
            new Thread(new Runnable() {
                @Override
                public void run() {
                    ...............耗时操作
                    Message message = Message.obtain();
                    message.what = 1;
                    mHandler.sendMessage(message);
                }
            }).start();
        }
    }
    

    sendMessage 调用sendMessageDelayed(msg, 0)最后调用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);
        }
    

    这里我有个问题 message是什么时候放到队列里的 是时间结束之后 还是立即放入等待?
    继续看代码

    boolean enqueueMessage(Message msg, long when) {
                 ......
                // 插入队列 按照执行顺序从头到尾
                if (p == null || when == 0 || when < p.when) {
                    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; // invariant: p == prev.next
                    prev.next = msg;
                    ......
                   // mPtr 标记在取消息的时候会用上
                  if (needWake) {
                    nativeWake(mPtr);
                 }
        }
    

    至此知道了消息封装了执行的时间 立即插入了队列(这样也导致了内存泄漏的问题),接着又有问题Looper怎么取出消息传送给handler的handleMessage处理呢?
    分析下Looper 大概都知道一个线程对应一个Looper Looper执行loop方法死循环取消息队列的消息

    public static void loop() {
            final Looper me = myLooper();
            ....
            for (;;) {
                Message msg = queue.next();  // might block
                if (msg == null) {
                    // 一般不会退出  调用Looper.quit()可以使next返回null
                    return;
                }
                final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
                final long traceTag = me.mTraceTag;
                if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                    Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
                }
                final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
                final long end;
                try {
                    // 发送message 给handler处理
                    msg.target.dispatchMessage(msg);
                    end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
                } finally {
                    if (traceTag != 0) {
                        Trace.traceEnd(traceTag);
                    }
                }
                ...
                msg.recycleUnchecked();
            }
        }
    

    但是这里没有涉及到延迟的处理 在代码里找 next方法怎么取消息 这里有关于延时的处理
    还有使死循环为什么不会造成ANR? 其实线程会在适当的时候阻塞 并不会一直死循环执行,onCreate/onStart/onResume等操作时间过长,会导致掉帧,甚至发生ANR,looper.loop本身不会导致应用卡死,队列有新消息时会唤醒线程,最新的消息在头部,会去执行最头部的消息,当执行到延时消息时又会被阻塞,有新消息又会被唤醒,如此往复,并不是一直死循环执行。

     Message next() {
            final long ptr = mPtr;
            if (ptr == 0) {
                 // loop退出或者dispose
                return null;
            }
            int pendingIdleHandlerCount = -1; // -1 only during first iteration
            int nextPollTimeoutMillis = 0;
            for (;;) {
                if (nextPollTimeoutMillis != 0) {
                    // 有还没到时间的消息没被处理时 调用
                    Binder.flushPendingCommands();
                }
                // 这是一个native 方法 大体意思是阻塞nextPollTimeoutMilli时间
                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) {
                            // 在这里如果执行时间没到 是不会返回message的
                            nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                        } else {
                            // 正确返回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;
                    }
    
                    // looper quit
                    if (mQuitting) {
                        dispose();
                        return null;
                    }
                    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;
            }
        }
    

    这样整个流程差不多走完了 其他细节方面需要精读下源码 - -
    为什么主线程不会导致应用卡死?参考https://www.zhihu.com/question/34652589

    相关文章

      网友评论

        本文标题:Android 消息机制

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