zoukankan      html  css  js  c++  java
  • Android 消息机制

    要理解原理, read the fucking source

    主要涉及到的类:Thread,Handler,Looper,MessageQueue,Message

    这几个类的关系图:


    发送消息流程图:

    1、从HandlerThread入手。

         HandlerThread是android系统提供的类。继承Thread,是一个线程,调用start()方法后。将运行run()方法。

         public void run() {
            mTid = Process.myTid();
            Looper.prepare();// #1
            synchronized (this) {
                mLooper = Looper.myLooper();// #2
                notifyAll();
            }
            Process.setThreadPriority(mPriority);
            onLooperPrepared();
            Looper.loop();// #3
            mTid = -1;
        }

     第1个标示中,是调用了Looper的prepare方法。

         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));// #4
        }

       在代码段的第4个标示中。将Looper实例存放在线程局部变量ThreadLocal中,将Looper和当前线程绑定。


       在代码段的第2个标示中,获取了与当前线程绑定的Looper实例,当前线程就拥有了Looper实例。

       在代码段的第3个标示中,调用了Looper的loop()方法。该方法是一个堵塞方法,loop()方法代码:

        /**
         * Run the message queue in this thread. Be sure to call
         * {@link #quit()} to end the loop.
         */
        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;
    
            // Make sure the identity of this thread is that of the local process,
            // and keep track of what that identity token actually is.
            Binder.clearCallingIdentity();
            final long ident = Binder.clearCallingIdentity();
    
            for (;;) {
                Message msg = queue.next(); // might block #5
                if (msg == null) {
                    // No message indicates that the message queue is quitting.
                    return;
                }
    
                // This must be in a local variable, in case a UI event sets the logger
                Printer logging = me.mLogging;
                if (logging != null) {
                    logging.println(">>>>> Dispatching to " + msg.target + " " +
                            msg.callback + ": " + msg.what);
                }
    
                msg.target.dispatchMessage(msg);#6
    
                if (logging != null) {
                    logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
                }
    
                // Make sure that during the course of dispatching the
                // identity of the thread wasn't corrupted.
                final long newIdent = Binder.clearCallingIdentity();
                if (ident != newIdent) {
                    Log.wtf(TAG, "Thread identity changed from 0x"
                            + Long.toHexString(ident) + " to 0x"
                            + Long.toHexString(newIdent) + " while dispatching to "
                            + msg.target.getClass().getName() + " "
                            + msg.callback + " what=" + msg.what);
                }
    
                msg.recycle();
            }
        }

     for(;;)是一个死循环,在代码段的标示5中,官方凝视queue.next()方法是一个堵塞方法,也就是会一直等待消息。  

     代码段的标示6中调用handler的dispatchMessage方法。发送消息,这里的handler是怎样和Message关联上的?这里做个埋笔。往下分析。

     也许有人会问,Message取完之后。在哪里唤醒该线程,然后继续循环获取Message呢?没错,是在调用Handler的sendMessage后。向MessageQueue中插入消息的时候唤醒,调用本地方法nativeWake(mPtr)。

     

    2、開始发送消息

    将Handler和Looper关联的代码:
    handlerThread.start();
    mLogicHandler = new LogicHandler(handlerThread.getLooper());

    Handler构造方法:
    public Handler(Looper looper, Callback callback, boolean async) {
         mLooper = looper;
         mQueue = looper.mQueue;
         mCallback = callback;
         mAsynchronous = async;
    }

    Handler就拥有了Looper和MessageQueue了。

    Handler中的发送消息方法:
    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);
    }
    enqueueMessage方法:
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
            msg.target = this;#7
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
            return queue.enqueueMessage(msg, uptimeMillis);#8
    }
    代码段中的标示7。将当前handler实例赋值给message的targer,这是上面埋点的解答。
    代码段中的标示8。将会调用MessageQueue的。

    MessageQueue的enqueueMessage方法
    final boolean enqueueMessage(Message msg, long when) {
            if (msg.isInUse()) {
                throw new AndroidRuntimeException(msg + " This message is already in use.");
            }
            if (msg.target == null) {
                throw new AndroidRuntimeException("Message must have a target.");
            }
    
            boolean needWake;
            synchronized (this) {
                if (mQuiting) {
                    RuntimeException e = new RuntimeException(
                            msg.target + " sending message to a Handler on a dead thread");
                    Log.w("MessageQueue", e.getMessage(), e);
                    return false;
                }
    
                msg.when = when;
                Message p = mMessages;
                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;
                }
            }
            if (needWake) {
                nativeWake(mPtr);// #9
            }
            return true;
        }


    在代码片段标示5中。调用了nativeWake(mPtr)方法,该方法是本地方法,用于唤醒Thread线程。

    參考资料:

    android的消息队列机制


     

  • 相关阅读:
    (01)Docker简介
    Gym-101242B:Branch Assignment(最短路,四边形不等式优化DP)
    2019牛客暑期多校训练营(第三场)G: Removing Stones(启发式分治)
    POJ
    高维前缀和
    HDU
    BZOJ
    HDU
    POJ
    Gym-100648B: Hie with the Pie(状态DP)
  • 原文地址:https://www.cnblogs.com/zsychanpin/p/7224381.html
Copyright © 2011-2022 走看看