Application of Producer and Consumer Model in Android

What

The so-called producer-consumer model is a model established through an intermediate buffer between a local brainless production and a local brainless consumption. This decoupling is not what many people want, and the key to decoupling is how to use the intermediate buffer. There are many examples in life, such as those selling mobile phones. They are only responsible for production, while we are only responsible for consumption. The buffer in the middle is their inventory. Another example is the post office, where we only write letters and the recipient receives letters. The buffer in the middle is the post office. Also, take the subway and punch in at work... Life is full of this model.


Producer and Consumer Model

With production and consumption, but the only thing that remains unchanged in the world is change. As a result, various problems arise, such as the inconsistency between producers and consumers, the control of time and the level of efficiency. This model is also used in many places in the beautiful big Android. Similarly, this problem will arise. How do we deal with these problems in Android? What does his buffer do?

How

First, let's see what applications of this model are commonly used in Android.

How many ways can Android update its UI in sub-threads?
When beginners see this, they should be proud to say:

1,runOnUiThread
2,view.post()
3,handler

runOnUiThread

view.post()

The source code of the first two modes implements the mHandler.post(action) method internally, which shows that these three modes are actually one way, through the Handler mechanism, about the Handler mechanism, please listen to the next decomposition.

There is also the most familiar Toast.


Toast internal source code

The interior is also Handler: mHandler. obtainMessage (0, windows Token). sendToTarget ();

Why

In fact, the core of Android message mechanism is Handler mechanism, and the realization of message mechanism model is producer-consumer model. So how does the Handler mechanism work?

Look at the source code all the way to track, pull out layer by layer of fog, you can see the shadow of producer-consumer model in MessageQueue,Message is the thing produced, and MessageQueue implements the production and consumption operation function.

MeesageQueue, see the code as follows:
enqueueMessage()

boolean enqueueMessage(Message msg, long when) {
    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) {
            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) {
            // 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;
}

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

The analysis is as follows:

Product


Message Link List. png

Producer:

 enqueueMessage()  The object of production is Message
          
       if(beforeMessag==null||when=0||when<beforeMessag.when){
                    initMessage;
       }else{//New message is queue entry operation
                prevMsg.next=curMsg;
       }

        Message p=Message mMessage;
        Message prev;
        loop  //Loop out the last message of the current list and assign it to prev.
          ->prev =p;
          ->p=p.next;
        //Assignment to Next
        msg.next=p=null;
        prev.next =msg;

Consumer:

next()
    loop
       ->Message prevMsg=null; Message msg=mMessages;
             //Move the next MSg up, and for loop move the remaining msgs one by one forward
       ->   if(prevMsg!=null) prevMsg.next=msg.next;
       ->     else mMessages=msg.next;//   Move up the main list by one msg
       ->    return msg;

1. enqueueMessage() is executed for the production line. enqueueMessage() enters the team and returns true.

2, next() is executed for the consumer thread, queue: in Looper.loop(), keep fetching, and in next(), return msg or wait as soon as it is fetched. Next adds a synchronization lock to ensure mutual exclusion with enqueue. Enqueue also adds synchronization locks to ensure mutual exclusion with next: add message to the Message list, and judge that if there is a blockage, wake-up operation is needed. Appropriate producer-consumer model.

summary

The essence of producers and consumers is:

Different threads operate different methods of the same object, but to maintain their mutual exclusion, there can be no deadlock. If the condition is satisfied, they will notify other waiting threads. If the condition is not satisfied, they will sleep and wait.

In Thread-1, the producer is only responsible for production, while in Thread-2, the consumer is only responsible for consumption. Operations are mutually exclusive. When the producer reaches the upper limit, the producer waits, whereas when the consumer reaches the upper limit, all threads wait.

[citation]
1. Inspiration of Model Interpretation: Poke Here to See the Explanation of the Great God
2. MessageQueue source code parsing
3. Toast Source Parsing, Emma, in the same order as I see it

Keywords: Android Mobile Windows

Added by esport on Fri, 07 Jun 2019 03:23:48 +0300