In simple terms, Handler surgical profiling mechanism (ThreadLocal, Looper, MessageQueen, Message) (lower)

[Imprint] please indicate the source (please respect the original, bloggers reserved the right to pursue)
http://blog.csdn.net/qq_24295537/article/details/77972598
by [the blog] yangLiHai_

In simple terms, Handler surgical profiling mechanism (ThreadLocal, Looper, MessageQueen, Message) (a)

This article is the second mechanism Handler, did not see the first chapter of a small partner, you can take a look at the top poke yo.
Previous We ThreadLocal and Looper were analyzed, and then the papers, talk about MessageQueen and Handler class.
MessageQueen and Message
MessageQueen are stored Message, the translation is called the message queue, but it is not an internal message queue, but a single linked list data structure, data is stored inside Message. MMseeages MessageQueen field is stored in the head node. Since it is a linked list, it must be related to insertion and removal, we look at its insertion methods:

boolean enqueueMessage(Message msg, long when) {
        synchronized (this) {
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                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 {
                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;
    }

This is the main structure of the code. The first parameter is the Message enqueueMessage, the second is a timestamp, he decided this Message which node is inserted and when performing callback. It will first determine whether the exit is, if the message is not inserted directly recovered and returned a failure. The Message When then determines to which node is inserted, if the first node is null, or when == 0, or when when less than the head node, then put the data into this header node. else inside the logic is similar, according to when the data to see if two of the insert intermediate the two data. Finally finished execution returns true, inserted successfully.

Take out the data is next, we specifically look at:

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

            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        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;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    nextPollTimeoutMillis = -1;
                }
                if (mQuitting) {
                    dispose();
                    return null;
                }

                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

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

            pendingIdleHandlerCount = 0;

            nextPollTimeoutMillis = 0;
        }
    }

Remove the data is accompanied by deleted, it is also an unconditional inside the for loop. First, to acquire from the first node, if no data if it blocked there, waiting for data to return native layer, after the return to the looper returns to process data, exit the loop mode is judged according mQuitting, this field is quit methods inside to change. The only method is called next Looper is in the loop () method, this method can be combined loop just said look, a glance. Attach the following code is the exit method, which is the method of Looper quit and quitSafely specific implementation of:

void quit(boolean safe) {
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }

        synchronized (this) {
            if (mQuitting) {
                return;
            }
            mQuitting = true;

            if (safe) {
                removeAllFutureMessagesLocked();
            } else {
                removeAllMessagesLocked();
            }

            // We can assume mPtr != 0 because mQuitting was previously false.
            nativeWake(mPtr);
        }
    }

MessageQueen Summary: MessageQueen There are many ways to involve the native layer, I did not analyze here, this is not a major impact on our understanding of the logic MessageQueen, interested look at themselves. MessageQueen inserted message is dependent timestamp field when, after insertion are orderly rows in which all of his messages, messages follow taken is obtained from a first start, so that each time the first node is taken, to obtain after the head node mMessages point to the next node, the first node acquires again the next, until all removed.

Handler
finished after this, we look for the Handler class, this is the whole handler mechanism exposed to our upper class, mainly to send a message and send all kinds of post, we specifically look at the code:

public final boolean post(Runnable r){
       return  sendMessageDelayed(getPostMessage(r), 0);
}

public final boolean sendMessage(Message msg)
{
    return sendMessageDelayed(msg, 0);
}

public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

public final boolean sendEmptyMessage(int what)
{
    return sendEmptyMessageDelayed(what, 0);
}

public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
 }

public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageAtTime(msg, uptimeMillis);
}

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

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
} 

Can be seen from the code, all the messages are sent back pointing enqueueMessage (queue, msg, uptimeMillis); this method, and this method is inserted in a message to the MessageQueen. Before inserting MessageQueen message, there is a line of code: msg.target = this, the handler is provided to msg, after insertion of a message, the next MessageQueen looper method returns to this message, to receive the looper Handler subsequent processing, the main code you can see that point go through msg.target.dispatchMessage (msg); we have said in speaking looper when the target is the handler. Not repeat them here, do not know the man himself to look at the source of it, linked glance.

Handler has several methods of construction, meaning not the same, we are talking about a separate case, this is our most popular construction method:


public interface Callback {
        public boolean handleMessage(Message msg);
}

public Handler() {
        this(null, false);
}

public Handler(Callback callback) {
        this(callback, false);
}

public Handler(boolean async) {
        this(null, async);
}

public Handler(Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {
        final Class<? extends Handler> klass = getClass();
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
            (klass.getModifiers() & Modifier.STATIC) == 0) {
        Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
            klass.getCanonicalName());
        }
    }

    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
        "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

In addition to new Handler (), we can also pass a callback to construct Handler, the callback is also a handleMessage (msg) method, so that you can callback the callback in all msg. In the constructor, you will go to check the current thread has no looper, no, they would have reported abnormal, which also happens to solve the issues we have raised in the opening time.
Handler There are several construction methods, you can pass in a Looper:

public Handler(Looper looper) {
        this(looper, null, false);
}

public Handler(Looper looper, Callback callback) {
        this(looper, callback, false);
}

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

Looper when constructing Handler, you can specify looper (This is not nonsense it) is by passing Handler, what use is it? Handler can switch to the callback we created looper thread, for example, you create a child thread Handler, but I hope gray tone function handleMessage method executed in the main thread, you can also build it:

private Handler mHandler = new Handler(Looper.getMainLooper()){
        @Override
        public void handleMessage(Message msg) {
            // TODO: 9/7/17 do what you wanna
        }
};

Handler of these rich construction method, can be very convenient to help us switch threads. Last Message Handler process are distributed via dispatchMessage, the specific code as follows:

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
}

 private static void handleCallback(Message message) {
        message.callback.run();
}

It can be seen, after the message came in, not the first time to handleMessage () method of treatment. I will first go to see whether msg.callback is null, if non-empty on the implementation of the run method and then return (this callback is a Runnable). Need to build such a Message:

Message msg = Message.obtain(mHandler, new Runnable() {
            @Override
            public void run() {
                // TODO: 9/13/17  ....
            }
});

Then take a look mCallback whether null, mCallback that we passed in the constructor Handler time:

 mHandler = new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                return false;
            }
});

Then by return mCallback.handleMessage method to determine whether to perform handleMessage Handler's ();
this is dispatchMessage logic, conclude msg.callback the highest priority, followed by Handler.mCallback, finally Handler.handleMessage.

Conclusion:
all mechanisms to process Handler finished here, if you follow the blog all read it again, then you certainly understand his principles. Handler For us, this may be a class Handler used, but run it inside a special thought worth learning, for improving the ability of our architecture helpful.

If there are inaccuracies, welcome that.

Published 17 original articles · won praise 12 · views 10000 +

Guess you like

Origin blog.csdn.net/qq_24295537/article/details/77972598