banner



How To Register Listener In Thread Loop

Affiliate 4. Thread Communication

In multithreaded appplications, tasks tin run in parallel and collaborate to produce a result. Hence, threads have to be able to communicate to enable truthful asynchronous processing. In Android, the importance of thread communication is emphasized in the platform-specific handler/looper mechanism that is the focus in this chapter, together with the traditional Java techniques. The chapter covers:

  • Passing data through a one-way data pipe
  • Shared memory communication
  • Implementing a consumer-producer pattern with BlockingQueue
  • Operations on bulletin queues
  • Sending tasks back to the UI Thread

Pipes

Pipes are a part of the java.io parcel. That is, they are general Coffee functionality and non Android specific. A pipe provides a mode for ii threads, within the same process, to connect and found a 1-style data channel. A producer thread writes data to the pipage, whereas a consumer thread reads data from the pipe.

Note

The Java pipage is comparable to the Unix and Linux pipe operator (the | beat graphic symbol) that is used to redirect the output from one control to the input for another control. The pipe operator works across processes in Linux, but Java pipes work beyond threads in the virtual motorcar, for example, within a process.

The pipe itself is a circular buffer allocated in retention, available but to the two connected threads. No other threads tin can access the data. Hence, thread safety—discussed in Thread Safety—is ensured. The piping is also one-directional, permitting just one thread to write and the other to read (Figure four-1).

Two threads connected through a pipe.

Effigy iv-one. Thread communication with pipes

Pipes are typically used when you take ii long-running tasks and i has to offload data to another continuously. Pipes arrive easy to decouple tasks to several threads, instead of having only ane thread handle many tasks. When i task has produced a result on a thread, it pipes the result on to the next thread that processes the data further. The gain comes from clean code separation and concurrent execution. Pipes tin can be used between worker threads and to offload work from the UI thread, which you desire to continue light to preserve a responsive user experience.

A pipe tin can transfer either binary or character information. Binary data transfer is represented by PipedOutputStream (in the producer) and PipedInputStream (in the consumer), whereas character data transfer is represented by PipedWriter (in the producer) and PipedReader (in the consumer). Apart from the data transfer type, the two pipes take similar functionality. The lifetime of the piping starts when either the writer or the reader thread establishes a connection, and it ends when the connection is closed.

Basic Pipe Use

The primal pipe life cycle can exist summarized in three steps: setup, data transfer (which can exist repeated every bit long as the ii threads want to exchange data), and disconnection. The following examples are created with PipedWriter/PipedReader, but the same steps piece of work with PipedOutputStream/PipedInputStream.

  1. Fix up the connexion:

                        PipedReader                    r                    =                    new                    PipedReader                    ();                    PipedWriter                    w                    =                    new                    PipedWriter                    ();                    w                    .                    connect                    (                    r                    );                  

    Here, the connectedness is established past the writer connecting to the reader. The connection could just also be established from the reader. Several constructors besides implicitly set up a pipe. The default buffer size is 1024 but is configurable from the consumer side of the pipe, as shown afterwards:

                        int                    BUFFER_SIZE_IN_CHARS                    =                    1024                    *                    4                    ;                    PipedReader                    r                    =                    new                    PipedReader                    (                    BUFFER_SIZE_IN_CHARS                    );                    PipedWriter                    w                    =                    new                    PipedWriter                    (                    r                    );                  
  2. Pass the reader to a processing thread:

                        Thread                    t                    =                    new                    MyReaderThread                    (                    r                    );                    t                    .                    start                    ();                  

    After the reader thread starts, it is gear up to receive data from the writer.

  3. Transfer data:

                        // Producer thread: Write unmarried character or array of characters                    w                    .                    write                    (                    'A'                    );                    // Consumer thread: Read the data                    int                    result                    =                    r                    .                    read                    ();                  

    Communication adheres to the consumer-producer blueprint with a blocking mechanism. If the pipe is total, the write() method will block until enough data has been read, and consequently removed from the pipe, to leave room for the data the author is trying to add. The read() method blocks whenever there is no data to read from the pipe. It's worth noticing that the read() method returns the character every bit an integer value to ensure that enough space is available to handle various encoding with different sizes. You tin can cast the integer value back to a graphic symbol.

    In practice, a better approach would await like this:

                        // Producer thread: Flush the piping later on a write.                    w                    .                    write                    (                    'A'                    );                    westward                    .                    flush                    ();                    // Consumer thread: Read the data in a loop.                    int                    i                    ;                    while                    ((                    i                    =                    reader                    .                    read                    ())                    !=                    -                    ane                    ){                    char                    c                    =                    (                    char                    )                    i                    ;                    // Handle received information                    }                  

    Calling flush() after a write to the pipe notifies the consumer thread that new data is available. This is useful from a performance perspective, because when the buffer is empty, the PipedReader uses a blocking phone call to wait() with 1-second timeout. Hence, if the flush() telephone call is omitted, the consumer thread may delay the reading of data up to 1 2nd. By calling affluent(), the producer cuts short the look in the consumer thread and allows data processing to go on immediately.

  4. Shut the connection.

    When the communication phase is finished, the pipe should be asunder:

                        // Producer thread: Close the author.                    w                    .                    close                    ();                    // Consumer thread: Close the reader.                    r                    .                    close                    ();                  

    If the author and reader are connected, it's enough to close merely i of them. If the writer is closed, the pipage is asunder but the data in the buffer tin still be read. If the reader is closed, the buffer is cleared.

Example: Text Processing on a Worker Thread

This next example illustrates how pipes can procedure text that a user enters in an EditText. To keep the UI thread responsive, each character entered by the user is passed to a worker thread, which presumably handles some time-consuming processing:

              public              form              PipeExampleActivity              extends              Activity              {              private              static              final              String              TAG              =              "PipeExampleActivity"              ;              individual              EditText              editText              ;              PipedReader              r              ;              PipedWriter              w              ;              private              Thread              workerThread              ;              public              void              onCreate              (              Bundle              savedInstanceState              )              {              super              .              onCreate              (              savedInstanceState              );              r              =              new              PipedReader              ();              w              =              new              PipedWriter              ();              endeavour              {              w              .              connect              (              r              );              }              catch              (              IOException              due east              )              {              e              .              printStackTrace              ();              }              setContentView              (              R              .              layout              .              activity_pipe              );              editText              =              (              EditText              )              findViewById              (              R              .              id              .              edit_text              );              editText              .              addTextChangedListener              (              new              TextWatcher              ()              {              @Override              public              void              beforeTextChanged              (              CharSequence              charSequence              ,              int              start              ,              int              count              ,              int              after              )              {              }              @Override              public              void              onTextChanged              (              CharSequence              charSequence              ,              int              first              ,              int              before              ,              int              count              )              {              attempt              {              // Only handle addition of characters              if              (              count              >              earlier              )              {              // Write the last entered character to the pipe              w              .              write              (              charSequence              .              subSequence              (              before              ,              count              ).              toString              ());              }              }              catch              (              IOException              e              )              {              e              .              printStackTrace              ();              }              }              @Override              public              void              afterTextChanged              (              Editable              editable              )              {              }              });              workerThread              =              new              Thread              (              new              TextHandlerTask              (              r              ));              workerThread              .              start              ();              }              @Override              protected              void              onDestroy              ()              {              super              .              onDestroy              ();              workerThread              .              interrupt              ();              try              {              r              .              shut              ();              w              .              close              ();              }              catch              (              IOException              e              )              {              }              }              individual              static              class              TextHandlerTask              implements              Runnable              {              private              concluding              PipedReader              reader              ;              public              TextHandlerTask              (              PipedReader              reader              ){              this              .              reader              =              reader              ;              }              @Override              public              void              run              ()              {              while              (              Thread              .              currentThread              ().              isInterrupted              ()){              effort              {              int              i              ;              while              ((              i              =              reader              .              read              ())              !=              -              1              ){              char              c              =              (              char              )              i              ;              //Add together TEXT PROCESSING LOGIC HERE              Log              .              d              (              TAG              ,              "char = "              +              c              );              }              }              catch              (              IOException              e              )              {              e              .              printStackTrace              ();              }              }              }              }              }            

When the PipeExampleActivity is created, information technology will show an EditText box, which has a listener (TextWatcher) for changes in the content. Whenever a new character is added in the EditText, the character volition be written to the pipe and read in the TextHandlerTask. The consumer job is an space loop that reads a character from the pipe every bit soon equally there is anything to read. The inner while-loop will block when calling read() if the pipe is empty.

Warning

Be conscientious when involving the UI thread with pipes, due to the possible blocking of calls if the pipe is either full (producer blocks on its write() call) or empty (consumer blocks on its read() telephone call).

Shared memory (using the memory area known in programming as the heap ) is a mutual way to laissez passer information between threads. All threads in an application tin can admission the aforementioned address infinite within the process. Hence, if one thread writes a value on a variable in the shared retentivity, it tin can be read by all the other threads, as shown in Figure 4-two.

Multiple threads accessing the same memory area.

Effigy four-2. Thread communication with shared retentiveness

If a thread stores information every bit a local variable, no other thread can see it. By storing it in shared retentivity, it tin can apply the variables for advice and share piece of work with other threads. Objects are stored in the shared memory if they are scoped every bit one of the following:

  • Instance fellow member variables
  • Form member variables
  • Objects declared in methods

The reference of an object is stored locally on the thread'south stack, but the object itself is stored in shared retention. The object is attainable from multiple threads just if the method publishes the reference outside the method telescopic, for example, by passing the reference to another object's method. Threads communicate through shared retentiveness past defining instance and class fields that are accessible from multiple threads.

Signaling

While threads are communicating through the state variables on the shared retentiveness, they could poll the state value to fetch changes to the country. But a more efficient machinery is the Java library's built-in signaling mechanism that lets a thread notify other threads of changes in the state. The signaling machinery varies depending on the synchronization type (see Table iv-1).

Table 4-one. Thread signaling

synchronized ReentrantLock ReentrantReadWriteLock

Blocking call, waiting for a state

Object.wait() Object.expect(timeout)

Condition.expect() Condition.look(timeout)

Condition.await() Status.expect(timeout)

Indicate blocked threads

Object.notify() Object.notifyAll()

Status.signal() Condition.signalAll()

Condition.signal() Condition.signalAll()

When a thread cannot continue execution until another thread reaches a specific state, it calls wait()/wait(timeout) or the equivalents await()/await(timeout), depending on the synchronization used. The timeout parameters indicate how long the calling thread should await before continuing the execution.

When another thread has changed the state, it signals the change with notify()/notifyAll() or the equivalents indicate()/signalAll(). Upon a signal, the waiting thread continues execution. The calls thus support 2 dissimilar pattern patterns that use conditions: the notify() or signal() version wakes one thread, called at random, whereas the notifyAll() or signalAll() version wakes all threads waiting on the signal.

Considering multiple threads could receive the signal and one could enter the critical section before the others wake, receiving the betoken does not guarantee that the right land is accomplished. A waiting thread should apply a design design where it checks that the wanted status is fulfilled earlier executing further. For case, if the shared state is protected with synchronization on the intrinsic lock, check the status before calling wait():

              synchronized              (              this              )              {              while              (              isConditionFulfilled              ==              false              )              {              wait              ();              }              // When the execution reaches this point,              // the land is correct.              }            

This pattern checks whether the status predicate is fulfilled. If not, the thread blocks by calling wait(). When another thread notifies on the monitor and the waiting thread wakes upwards, it checks again whether the condition has been fulfilled and, if non, it blocks again, waiting for a new point.

Warning

A very mutual Android apply case is to create a worker thread from the UI thread and let the worker thread produce a result to be used by some UI chemical element, so the UI thread should wait for the upshot. However, the UI thread should not await for a signal from a background thread, as it may block the UI thread. Instead, utilise the Android bulletin passing mechanism discussed later.

BlockingQueue

Thread signaling is a low-level, highly configurable mechanism that tin can be adapted to fit many utilize cases, but information technology may besides be considered as the well-nigh error-prone technique. Therefore, the Coffee platform builds loftier-level abstractions upon the thread signaling mechanism to solve one-directional handoff of capricious objects betwixt threads. The abstraction is oft called "solving the producer-consumer synchronization problem." The problem consists of use cases where there can be threads producing content (producer threads) and threads consuming content (consumer threads). The producers hand off messages for the consumers to process. The intermediator between the threads is a queue with blocking beliefs, i.e., java.util.concurrent.BlockingQueue (encounter Figure 4-3).

Producer and consumer threads communicating via a BlockingQueue.

Figure 4-3. Thread communication with BlockingQueue

The BlockingQueue acts as the coordinator between the producer and consumer threads, wrapping a list implementation together with thread signaling. The list contains a configurable number of elements that the producing threads fill with arbitrary data messages. On the other side, the consumer threads excerpt the messages in the order that they were enqueued and then procedure them. Coordination between the producers and consumers is necessary if they go out of sync, for example, if the producers manus off more than messages than the consumers can handle. So BlockingQueue uses thread conditions to ensure that producers cannot enqueue new messages if the BlockingQueue list is full, and that consumers know when at that place are messages to fetch. Synchronization betwixt the threads can be achieved with thread signaling, every bit Case: Consumer and Producer shows. But the BlockingQueue both blocks threads and signals the important state changes—i.east., the list is non full and the list is non empty.

The consumer-producer pattern implemented with the LinkedBlockingQueue-implementation is hands implemented by adding messages to the queue with put(), and removing them with take(), where put() blocks the caller if the queue is full, and take() blocks the caller if the queue is empty:

            public            class            ConsumerProducer            {            individual            final            int            LIMIT            =            10            ;            private            BlockingQueue            <            Integer            >            blockingQueue            =            new            LinkedBlockingQueue            <            Integer            >(            LIMIT            );            public            void            produce            ()            throws            InterruptedException            {            int            value            =            0            ;            while            (            true            )            {            blockingQueue            .            put            (            value            ++);            }            }            public            void            consume            ()            throws            InterruptedException            {            while            (            truthful            )            {            int            value            =            blockingQueue            .            take            ();            }            }            }          

Android Message Passing

So far, the thread communication options discussed have been regular Coffee, available in whatsoever Java awarding. The mechanisms—pipes, shared memory, and blocking queues—apply to Android applications just impose bug for the UI thread because of their tendency to block. The UI thread responsiveness is at risk when using mechanisms with blocking beliefs, because that may occasionally hang the thread.

The most common thread communication use case in Android is between the UI thread and worker threads. Hence, the Android platform defines its ain message passing machinery for communication betwixt threads. The UI thread can offload long tasks by sending data messages to be candy on background threads. The message passing mechanism is a nonblocking consumer-producer design, where neither the producer thread nor the consumer thread will block during the bulletin handoff.

The message handling mechanism is fundamental in the Android platform and the API is located in the android.os bundle, with a prepare of classes shown in Figure iv-four that implement the functionality.

Overview of the Android message passing components.

Effigy 4-4. API overview

android.os.Looper
A message dispatcher associated with the one and just consumer thread.
android.os.Handler
Consumer thread message processor, and the interface for a producer thread to insert messages into the queue. A Looper tin have many associated handlers, but they all insert messages into the same queue.
android.os.MessageQueue
Unbounded linked list of messages to be processed on the consumer thread. Every Looper—and Thread—has at nigh i MessageQueue.
android.os.Bulletin
Message to exist executed on the consumer thread.

Letters are inserted past producer threads and processed by the consumer thread, as illustrated in Figure four-5.

  1. Insert: The producer thread inserts messages in the queue past using the Handler continued to the consumer thread, every bit shown in Handler.
  2. Recollect: The Looper, discussed in Looper, runs in the consumer thread and retrieves letters from the queue in a sequential social club.
  3. Acceleration: The handlers are responsible for processing the letters on the consumer thread. A thread may take multiple Handler instances for processing letters; the Looper ensures that messages are dispatched to the right Handler.

Producer threads and consumer thread communicating via a MessageQueue.

Figure iv-5. Overview of the message-passing mechanism betwixt multiple producer threads and 1 consumer thread. Every message refers to to the next bulletin in the queue, here indicated by a left-pointing arrow.

Example: Basic Bulletin Passing

Before we dissect the components in detail, permit's wait at a primal message passing case to get united states acquainted with the code setup.

The following code implements what is probably one of the most common utilise cases. The user presses a push button on the screen that could trigger a long functioning, such as a network operation. To avoid stalling the rendering of the UI, the long operation, represented here by a dummy doLongRunningOperation() method, has to exist executed on a worker thread. Hence, the setup is merely one producer thread (the UI thread) and ane consumer thread (LooperThread).

Our code sets up a message queue. It handles the button click as usual in the onClick() callback, which executes on the UI thread. In our implementation, the callback inserts a dummy bulletin into the message queue. For sake of brevity, layouts and UI components have been left out of the example code:

              public              class              LooperActivity              extends              Action              {              LooperThread              mLooperThread              ;              private              static              grade              LooperThread              extends              Thread              {              1              public              Handler              mHandler              ;              public              void              run              ()              {              Looper              .              prepare              ();              2              mHandler              =              new              Handler              ()              {              3              public              void              handleMessage              (              Message              msg              )              {              4              if              (              msg              .              what              ==              0              )              {              doLongRunningOperation              ();              }              }              };              Looper              .              loop              ();              5              }              }              public              void              onCreate              (              Packet              savedInstanceState              )              {              super              .              onCreate              (              savedInstanceState              );              mLooperThread              =              new              LooperThread              ();              6              mLooperThread              .              kickoff              ();              }              public              void              onClick              (              View              five              )              {              if              (              mLooperThread              .              mHandler              !=              null              )              {              7              Bulletin              msg              =              mLooperThread              .              mHandler              .              obtainMessage              (              0              );              8              mLooperThread              .              mHandler              .              sendMessage              (              msg              );              9              }              }              private              void              doLongRunningOperation              ()              {              // Add long running operation here.              }              protected              void              onDestroy              ()              {              mLooperThread              .              mHandler              .              getLooper              ().              quit              ();              10              }              }            
1

Definition of the worker thread, acting equally a consumer of the message queue.

2

Acquaintance a Looper—and implicitly a MessageQueue—with the thread.

3

Ready a Handler to be used by the producer for inserting messages in the queue. Here we employ the default constructor so it volition bind to the Looper of the current thread. Hence, this Handler tin can created only afterwards Looper.set up(), or it will have nothing to bind to.

4

Callback that runs when the bulletin has been dispatched to the worker thread. It checks the what parameter and then executes the long functioning.

5

First dispatching messages from the message queue to the consumer thread. This is a blocking call, then the worker thread volition non finish.

6

Start the worker thread, so that it is ready to process messages.

7

There is race status between the setup of mHandler on a background thread and this usage on the UI thread. Hence, validate that mHandler is available.

8

Initialize a Message-object with the what argument arbitrarily set to 0.

9

Insert the message in the queue.

10

Cease the background thread. The call to Looper.quit() stops the dispatching of messages and releases Looper.loop() from blocking and so the run method tin can terminate, leading to the termination of the thread.

Classes Used in Message Passing

Let'south accept a more than detailed look now at the specific components of message passing and their use.

MessageQueue

The bulletin queue is represented by the android.os.MessageQueue class. It is congenital with linked messages, constituting an unbound one-directional linked list. Producer threads insert letters that will later on be dispatched to the consumer. The messages are sorted based on timestamps. The pending message with the lowest timestamp value is offset in line for acceleration to the consumer. However, a message is dispatched just if the timestamp value is less than the current time. If not, the dispatch will wait until the electric current fourth dimension has passed the timestamp value.

Effigy 4-6 illustrates a bulletin queue with three pending messages, sorted with timestamps where t1 < t2 < t3. Only 1 message has passed the dispatch barrier, which is the current time. Letters eligible for acceleration have a timestamp value less than the current time (represented by "Now" in the effigy).

MessageQueue with three pending messages.

Effigy four-vi. Pending messages in the queue. The rightmost message is outset in queue to be processed. The message arrows denote references to the next message in the queue.

If no bulletin has passed the acceleration barrier when the Looper is prepare to remember the adjacent bulletin, the consumer thread blocks. Execution is resumed as soon every bit a message passes the dispatch barrier.

The producers can insert new messages in the queue at whatever time and on whatsoever position in the queue. The insert position in the queue is based on the timestamp value. If a new message has the everyman timestamp value compared to the pending messages in the queue, it will occupy the first position in the queue, which is next to exist dispatched. Insertions always conform to the timestamp sorting social club. Message insertion is discussed farther in Handler.

MessageQueue.IdleHandler

If there is no bulletin to process, a consumer thread has some idle time. For instance, Effigy 4-7 illustrates a time slot where the consumer thread is idle. Past default, the consumer thread simply waits for new messages during idle fourth dimension; just instead of waiting, the thread can be utilized to execute other tasks during these idle slots. This characteristic tin can be utilized to let noncritical tasks postpone their execution until no other messages are competing for execution fourth dimension.

Idle time slot.

Figure 4-7. If no message has passed the dispatch bulwark, in that location is a time slot that can be utilized for execution before the next pending message needs to exist executed

When a pending message has been dispatched, and no other message has passed the dispatch barrier, a time slot occurs where the consumer thread tin can be utilized for execution of other tasks. An awarding gets hold of this time slot with the android.os.MessageQueue.IdleHandler-interface, a listener that generates callbacks when the consumer thread is idle. The listener is attached to the MessageQueue and detached from it through the following calls:

                // Get the bulletin queue of the current thread.                MessageQueue                mq                =                Looper                .                myQueue                ();                // Create and annals an idle listener.                MessageQueue                .                IdleHandler                idleHandler                =                new                MessageQueue                .                IdleHandler                ();                mq                .                addIdleHandler                (                idleHandler                )                // Unregister an idle listener.                mq                .                removeIdleHandler                (                idleHandler                )              

The idle handler interface consists of one callback method only:

                interface                IdleHandler                {                boolean                queueIdle                ();                }              

When the message queue detects idle fourth dimension for the consumer thread, it invokes queueIdle() on all registered IdleHandler-instances. It is up to the application to implement the callback responsibly. Y'all should usually avoid long-running tasks because they will delay awaiting messages during the time they run.

The implementation of queueIdle() must return a Boolean value with the following meanings:

truthful
The idle handler is kept agile; it will go on to receive callbacks for successive idle time slots.
false
The idle handler is inactive; it volition not receive anymore callbacks for successive idle time slots. This is the same thing every bit removing the listener through MessageQueue.removeIdleHandler().

Example: Using IdleHandler to terminate an unused thread

All registered IdleHandlers to a MessageQueue are invoked when a thread has idle slots, where it waits for new letters to process. The idle slots can occur earlier the first message, between messages, and later the concluding bulletin. If multiple content producers should process data sequentially on a consumer thread, the IdleHandler can be used to terminate the consumer thread when all messages are processed and then that the unused thread does not linger in memory. With the IdleHandler, it is not necessary to keep runway of the terminal inserted bulletin to know when the thread can be terminated.

Warning

This utilize case applies only when the producing threads insert messages in the MessageQueue without delay, so that the consumer thread is never idle until the last message is inserted.

The ConsumeAndQuitThread method shows the structure of a consuming thread with Looper and MessageQueue that terminates the thread when there are no more letters to procedure:

                public                grade                ConsumeAndQuitThread                extends                Thread                implements                MessageQueue                .                IdleHandler                {                individual                static                final                String                THREAD_NAME                =                "ConsumeAndQuitThread"                ;                public                Handler                mConsumerHandler                ;                private                boolean                mIsFirstIdle                =                truthful                ;                public                ConsumeAndQuitThread                ()                {                super                (                THREAD_NAME                );                }                @Override                public                void                run                ()                {                Looper                .                set                ();                mConsumerHandler                =                new                Handler                ()                {                @Override                public                void                handleMessage                (                Message                msg                )                {                // Swallow information                }                };                Looper                .                myQueue                ().                addIdleHandler                (                this                );                1                Looper                .                loop                ();                }                @Override                public                boolean                queueIdle                ()                {                if                (                mIsFirstIdle                )                {                2                mIsFirstIdle                =                false                ;                return                true                ;                3                }                mConsumerHandler                .                getLooper                ().                quit                ();                4                return                false                ;                }                public                void                enqueueData                (                int                i                )                {                mConsumerHandler                .                sendEmptyMessage                (                i                );                }                }              
1

Register the IdleHandler on the groundwork thread when it is started and the Looper is prepared so that the MessageQueue is prepare up.

2

Let the first queueIdle invocation pass, since it occurs before the first message is received.

3

Render truthful on the first invocation so that the IdleHandler still is registered.

4

Terminate the thread.

The bulletin insertion is done from multiple threads concurrently, with a false randomness of the insertion time:

                concluding                ConsumeAndQuitThread                consumeAndQuitThread                =                new                ConsumeAndQuitThread                ();                consumeAndQuitThread                .                start                ();                for                (                int                i                =                0                ;                i                <                10                ;                i                ++)                {                new                Thread                (                new                Runnable                ()                {                @Override                public                void                run                ()                {                for                (                int                i                =                0                ;                i                <                10                ;                i                ++)                {                SystemClock                .                slumber                (                new                Random                ().                nextInt                (                10                ));                consumeAndQuitThread                .                enqueueData                (                i                );                }                }                }).                get-go                ();              

Bulletin

Each item on the MessageQueue is of the android.os.Message class. This is a container object carrying either a data item or a task, never both. Data is processed past the consumer thread, whereas a task is simply executed when information technology is dequeued and y'all have no other processing to practice:

Note

The message knows its recipient processor—i.e., Handler—and tin can enqueue itself through Message.sendToTarget():

                Message                m                =                Bulletin                .                obtain                (                handler                ,                runnable                );                m                .                sendToTarget                ();              

As nosotros will see in Handler, the handler is most normally used for bulletin enqueuing, as it offers more than flexibility with regard to message insertion.

Data message

The data gear up has multiple parameters that can be handed off to the consumer thread, as shown in Table 4-2.

Table 4-2. Message parameters

Parameter proper name Type Usage

what

int

Message identifier. Communicates intention of the bulletin.

arg1, arg2

int

Simple data values to handle the common utilise case of handing over integers. If a maximum of 2 integer values are to exist passed to the consumer, these parameters are more efficient than allocating a Package, as explained under the data parameter.

obj

Object

Arbitrary object. If the object is handed off to a thread in another process, it has to implement Parcelable.

data

Parcel

Container of arbitrary data values.

replyTo

Messenger

Reference to Handler in some other procedure. Enables interprocess bulletin communication, as described in Two-Style Communication.

callback

Runnable

Task to execute on a thread. This is an internal case field that holds the Runnable object from the Handler.post methods in Handler.

Job message
The task is represented by a coffee.lang.Runnable object to be executed on the consumer thread. Chore letters cannot contain any data across the job itself.

A MessageQueue can incorporate any combination of data and job letters. The consumer thread processes them in a sequential way, independent of the type. If a message is a data message, the consumer processes the data. Chore messages are handled by letting the Runnable execute on the consumer thread, simply the consumer thread does not receive a message to exist processed in Handler.handleMessage(Bulletin), as it does with data messages.

The lifecycle of a message is uncomplicated: the producer creates the message, and eventually it is processed by the consumer. This description suffices for most use cases, but when a trouble arises, a deeper agreement of bulletin handling is invaluable. Let us take a await into what really happens with the message during its lifecycle, which can be split up into iv main states shown in Figure 4-8. The runtime stores message objects in an application-wide pool to enable the reuse of previous messages; this avoids the overhead of creating new instances for every handoff. The message object execution time is normally very short, and many letters are processed per fourth dimension unit.

Message lifecycle states.

Figure 4-8. Message lifecycle states

The state transfers are partly controlled by the application and partly by the platform. Notation that the states are not observable, and an application cannot follow the changes from one state to another (although there are means to follow the movement of messages, explained in Observing the Message Queue). Therefore, an application should not make whatever assumptions near the current state when treatment a message.

Initialized

In the initialized state, a bulletin object with mutable state has been created and, if it is a information message, populated with data. The application is responsible for creating the message object using ane of the following calls. They take an object from the object puddle:

  • Explicit object construction:

                          Message                      m                      =                      new                      Message                      ();                    
  • Factory methods:

    • Empty message:

                                  Message                            m                            =                            Message                            .                            obtain                            ();                          
    • Data bulletin:

                                  Message                            m                            =                            Message                            .                            obtain                            (                            Handler                            h                            );                            Message                            one thousand                            =                            Message                            .                            obtain                            (                            Handler                            h                            ,                            int                            what                            );                            Bulletin                            1000                            =                            Bulletin                            .                            obtain                            (                            Handler                            h                            ,                            int                            what                            ,                            Object                            o                            );                            Message                            m                            =                            Bulletin                            .                            obtain                            (                            Handler                            h                            ,                            int                            what                            ,                            int                            arg1                            ,                            int                            arg2                            );                            Message                            m                            =                            Message                            .                            obtain                            (                            Handler                            h                            ,                            int                            what                            ,                            int                            arg1                            ,                            int                            arg2                            ,                            Object                            o                            );                          
    • Task message:

                                  Message                            m                            =                            Message                            .                            obtain                            (                            Handler                            h                            ,                            Runnable                            task                            );                          
    • Re-create constructor:

                                  Message                            m                            =                            Message                            .                            obtain                            (                            Message                            originalMsg                            );                          

Awaiting

The message has been inserted into the queue by the producer thread, and information technology is waiting to be dispatched to the consumer thread.

Dispatched

In this country, the Looper has retrieved and removed the message from the queue. The message has been dispatched to the consumer thread and is currently being processed. At that place is no application API for this performance because the acceleration is controlled by the Looper , without the influence of the awarding. When the Looper dispatches a bulletin, it checks the commitment data of the message and delivers the message to the right recipient. Once dispatched, the message is executed on the consumer thread.

Recycled

At this point in the lifecycle, the message country is cleared and the example is returned to the message pool. The Looper handles the recycling of the message when information technology has finished executing on the consumer thread. Recycling of messages is handled by the runtime and should not exist done explicitly by the application.

Note

Once a message is inserted in the queue, the content should non be contradistinct. In theory, it is valid to change the content before the message is dispatched. However, because the country is non observable, the message may exist processed past the consumer thread while the producer tries to change the data, raising thread condom concerns. It would be even worse if the bulletin has been recycled, because information technology then has been returned to the bulletin pool and possibly used by another producer to pass data in another queue.

Looper

The android.os.Looper class handles the dispatch of messages in the queue to the associated handler. All letters that have passed the acceleration barrier, as illustrated in Figure 4-half-dozen, are eligible for acceleration by the Looper. As long as the queue has messages eligible for dispatch, the Looper will ensure that the consumer thread receives the messages. When no messages accept passed the dispatch barrier, the consumer thread will cake until a message has passed the acceleration barrier.

The consumer thread does non collaborate with the message queue directly to retrieve the messages. Instead, a bulletin queue is added to the thread when the Looper has been fastened. The Looper manages the message queue and facilitates the dispatch of letters to the consumer thread.

By default, simply the UI thread has a Looper; threads created in the awarding demand to become a Looper associated explicitly. When the Looper is created for a thread, information technology is continued to a message queue. The Looper acts every bit the intermediator between the queue and the thread. The setup is done in the run method of the thread:

              class              ConsumerThread              extends              Thread              {              @Override              public              void              run              ()              {              Looper              .              fix              ();              1              // Handler cosmos omitted.              Looper              .              loop              ();              2              }              }            
1

The first stride is to create the Looper, which is done with the static prepare() method; it will create a message queue and acquaintance it with the current thread. At this point, the message queue is gear up for insertion of letters, but they are not dispatched to the consumer thread.

2

Start treatment messages in the bulletin queue. This is a blocking method that ensures the run() method is not finished; while run() blocks, the Looper dispatches messages to the consumer thread for processing.

A thread can have simply ane associated Looper; a runtime mistake will occur if the awarding tries to set up a 2nd one. Consequently, a thread can have merely one message queue, meaning that messages sent by multiple producer threads are processed sequentially on the consumer thread. Hence, the currently executing bulletin volition postpone subsequent messages until it has been processed. Letters with long execution times shall non exist used if they can filibuster other of import tasks in the queue.

Looper termination

The Looper is requested to stop processing letters with either quit or quitSafely: quit() stops the looper from dispatching whatever more letters from the queue; all awaiting messages in the queue, including those that take passed the dispatch barrier, will exist discarded. quitSafely, on the other hand, only discards the messages that have not passed the dispatch barrier. Awaiting messages that are eligible for acceleration will be candy before the Looper is terminated.

Note

quitSafely was added in API level 18 (Jelly Bean iv.3). Previous API levels only support quit.

Terminating a Looper does not cease the thread; it just exits Looper.loop() and lets the thread resume running in the method that invoked the loop phone call. But you cannot start the former Looper or a new one, and so the thread can no longer enqueue or handle messages. If you telephone call Looper.prepare(), it volition throw RuntimeException because the thread already has an fastened Looper. If you telephone call Looper.loop(), it will block, but no messages will be dispatched from the queue.

The UI thread Looper

The UI thread is the merely thread with an associated Looper past default. It is a regular thread, like whatsoever other thread created by the awarding itself, only the Looper is associated with the thread[7] earlier the application components are initialized.

At that place are a few practical differences between the UI thread Looper and other application thread loopers:

  • Information technology is attainable from everywhere, through the Looper.getMainLooper() method.
  • It cannot be terminated. Looper.quit() throws RuntimeException.
  • The runtime associates a Looper to the UI thread by Looper.prepareMainLooper(). This can be washed only once per application. Thus, trying to adhere the master looper to another thread volition throw an exception.

Handler

And so far, the focus has been on the internals of Android thread advice, but an application mostly interacts with the android.os.Handler form. It is a two-sided API that both handles the insertion of messages into the queue and the message processing. As indicated in Figure 4-v, it is invoked from both the producer and consumer thread typically used for:

  • Creating messages
  • Inserting letters into the queue
  • Processing letters on the consumer thread
  • Managing messages in the queue

Setup

While conveying out its responsibilities, the Handler interacts with the Looper, message queue, and message. As Figure 4-4 illustrates, the but direct instance relation is to the Looper, which is used to connect to the MessageQueue. Without a Looper, handlers cannot function; they cannot couple with a queue to insert messages, and consequently they will not receive any messages to process. Hence, a Handler instance is already jump to a Looper instance at construction time:

  • Constructors without an explicit Looper bind to the Looper of the electric current thread:

                          new                      Handler                      ();                      new                      Handler                      (                      Handler                      .                      Callback                      )                    
  • Constructors with an explicit Looper bind to that Looper:

                          new                      Handler                      (                      Looper                      );                      new                      Handler                      (                      Looper                      ,                      Handler                      .                      Callback                      );                    

If the constructors without an explicit Looper are chosen on a thread without a Looper (i.e., it has non called Looper.set up()), there is nothing handlers can bind to, leading to a RuntimeException. Once a handler is bound to a Looper, the binding is concluding.

A thread can take multiple handlers; messages from them coexist in the queue simply are dispatched to the right Handler instance, every bit shown in Figure iv-nine.

Multiple handlers.

Figure 4-9. Multiple handlers using one Looper. The handler inserting a message is the same handler that processes the message.

Note

Multiple handlers volition non enable concurrent execution. The letters are however in the same queue and are processed sequentially.

Bulletin creation

For simplicity, the Handler class offers wrapper functions for the factory methods shown in Initialized to create objects of the Message form:

                Message                obtainMessage                (                int                what                ,                int                arg1                ,                int                arg2                )                Message                obtainMessage                ()                Bulletin                obtainMessage                (                int                what                ,                int                arg1                ,                int                arg2                ,                Object                obj                )                Bulletin                obtainMessage                (                int                what                )                Bulletin                obtainMessage                (                int                what                ,                Object                obj                )              

The message obtained from a Handler is retrieved from the message pool and implicitly continued to the Handler instance that requested information technology. This connection enables the Looper to dispatch each message to the right Handler.

Message insertion

The Handler inserts messages in the message queue in diverse ways depending on the message blazon. Job messages are inserted through methods that are prefixed mail , whereas data insertion methods are prefixed transport :

  • Add a job to the message queue:

                          boolean                      mail                      (                      Runnable                      r                      )                      f                      boolean                      postAtFrontOfQueue                      (                      Runnable                      r                      )                      boolean                      postAtTime                      (                      Runnable                      r                      ,                      Object                      token                      ,                      long                      uptimeMillis                      )                      boolean                      postAtTime                      (                      Runnable                      r                      ,                      long                      uptimeMillis                      )                      boolean                      postDelayed                      (                      Runnable                      r                      ,                      long                      delayMillis                      )                    
  • Add together a data object to the message queue:

                          boolean                      sendMessage                      (                      Message                      msg                      )                      boolean                      sendMessageAtFrontOfQueue                      (                      Bulletin                      msg                      )                      boolean                      sendMessageAtTime                      (                      Bulletin                      msg                      ,                      long                      uptimeMillis                      )                      boolean                      sendMessageDelayed                      (                      Message                      msg                      ,                      long                      delayMillis                      )                    
  • Add together simple information object to the message queue:

                          boolean                      sendEmptyMessage                      (                      int                      what                      )                      boolean                      sendEmptyMessageAtTime                      (                      int                      what                      ,                      long                      uptimeMillis                      )                      boolean                      sendEmptyMessageDelayed                      (                      int                      what                      ,                      long                      delayMillis                      )                    

All insertion methods put a new Message object in the queue, even though the application does non create the Bulletin object explicitly. The objects, such every bit Runnable in a chore post and what in a send, are wrapped into Message objects, because those are the only information types allowed in the queue.

Every message inserted in the queue comes with a time parameter indicating the fourth dimension when the message is eligible for acceleration to the consumer thread. The sorting is based on the time parameter, and information technology is the simply manner an application tin can affect the acceleration order:

default
Immediately eligible for dispatch.
at_front
This message is eligible for dispatch at time 0. Hence, it will be the next dispatched message, unless some other is inserted at the front before this 1 is processed.
filibuster
The corporeality of fourth dimension after which this message is eligible for dispatch.
uptime
The absolute time at which this message is eligible for dispatch.

Even though explicit delays or uptimes can be specified, the time required to process each message is still indeterminate. It depends both on whatever existing messages need to be processed first and the operating organization scheduling.

Inserting a message in the queue is not failsafe. Some common errors that can occur are listed in Tabular array 4-iii.

Table 4-three. Message insertion errors

Failure Error response Typical application problem

Message has no Handler.

RuntimeException

Message was created from a Message.obtain() method without a specified Handler.

Message has already been dispatched and is being processed.

RuntimeException

The same message instance was inserted twice.

Looper has exited.

Return faux

Bulletin is inserted afterwards Looper.quit() has been called.

Warning

The dispatchMessage method of the Handler grade is used past the Looper to acceleration messages to the consumer thread. If used past the awarding directly, the bulletin will be candy immediately on the calling thread and not the consumer thread.

Example: 2-way message passing

The HandlerExampleActivity simulates a long-running functioning that is started when the user clicks a button. The long-running chore is executed on a groundwork thread; meanwhile, the UI displays a progress bar that is removed when the background thread reports the result back to the UI thread.

First, the setup of the Action:

                public                class                HandlerExampleActivity                extends                Action                {                private                final                static                int                SHOW_PROGRESS_BAR                =                1                ;                private                last                static                int                HIDE_PROGRESS_BAR                =                0                ;                individual                BackgroundThread                mBackgroundThread                ;                private                TextView                mText                ;                private                Push button                mButton                ;                private                ProgressBar                mProgressBar                ;                @Override                public                void                onCreate                (                Bundle                savedInstanceState                )                {                super                .                onCreate                (                savedInstanceState                );                setContentView                (                R                .                layout                .                activity_handler_example                );                mBackgroundThread                =                new                BackgroundThread                ();                mBackgroundThread                .                start                ();                1                mText                =                (                TextView                )                findViewById                (                R                .                id                .                text                );                mProgressBar                =                (                ProgressBar                )                findViewById                (                R                .                id                .                progress                );                mButton                =                (                Push button                )                findViewById                (                R                .                id                .                push button                );                mButton                .                setOnClickListener                (                new                OnClickListener                ()                {                @Override                public                void                onClick                (                View                5                )                {                mBackgroundThread                .                doWork                ();                2                }                });                }                @Override                protected                void                onDestroy                ()                {                super                .                onDestroy                ();                mBackgroundThread                .                exit                ();                3                }                // ... The balance of the Activity is defined further downwardly                }              
1

A background thread with a message queue is started when the HandlerExampleActivity is created. It handles tasks from the UI thread.

2

When the user clicks a button, a new task is sent to the background thread. As the tasks will be executed sequentially on the background thread, multiple push clicks may lead to queueing of tasks earlier they are candy.

3

The background thread is stopped when the HandlerExampleActivity is destroyed.

BackgroundThread is used to offload tasks from the UI thread. It runs—and can receive messages—during the lifetime of the HandlerExampleActivity. It does not expose its internal Handler; instead it wraps all accesses to the Handler in public methods doWork and exit:

                individual                class                BackgroundThread                extends                Thread                {                private                Handler                mBackgroundHandler                ;                public                void                run                ()                {                1                Looper                .                prepare                ();                mBackgroundHandler                =                new                Handler                ();                2                Looper                .                loop                ();                }                public                void                doWork                ()                {                mBackgroundHandler                .                post                (                new                Runnable                ()                {                3                @Override                public                void                run                ()                {                Message                uiMsg                =                mUiHandler                .                obtainMessage                (                SHOW_PROGRESS_BAR                ,                0                ,                0                ,                null                );                4                mUiHandler                .                sendMessage                (                uiMsg                );                5                Random                r                =                new                Random                ();                int                randomInt                =                r                .                nextInt                (                5000                );                SystemClock                .                slumber                (                randomInt                );                6                uiMsg                =                mUiHandler                .                obtainMessage                (                HIDE_PROGRESS_BAR                ,                randomInt                ,                0                ,                zip                );                7                mUiHandler                .                sendMessage                (                uiMsg                );                8                }                });                }                public                void                leave                ()                {                9                mBackgroundHandler                .                getLooper                ().                quit                ();                }                }              
1

Associate a Looper with the thread.

2

The Handler processes simply Runnables. Hence, it is non required to implement Handler.handleMessage.

3

Post a long task to be executed in the background.

4

Create a Message object that contains only a what statement with a command—SHOW_PROGRESS_BAR—to the UI thread so that it tin can testify the progress bar.

5

Send the start message to the UI thread.

6

Simulate a long task of random length, that produces some data randomInt.

7

Create a Bulletin object with the issue randomInt, that is passed in the arg1 parameter. The what parameter contains a command—HIDE_PROGRESS_BAR—to remove the progress bar.

8

The message with the finish issue that both informs the UI thread that the task is finished and delivers a result.

9

Quit the Looper and so that the thread can stop.

The UI thread defines its own Handler that tin receive commands to control the progress bar and update the UI with results from the background thread:

                private                final                Handler                mUiHandler                =                new                Handler                ()                {                public                void                handleMessage                (                Message                msg                )                {                switch                (                msg                .                what                )                {                case                SHOW_PROGRESS_BAR:                1                mProgressBar                .                setVisibility                (                View                .                VISIBLE                );                break                ;                case                HIDE_PROGRESS_BAR:                2                mText                .                setText                (                String                .                valueOf                (                msg                .                arg1                ));                mProgressBar                .                setVisibility                (                View                .                INVISIBLE                );                intermission                ;                }                }                };              
1

Show the progress bar.

2

Hide the progress bar and update the TextView with the produced result.

Message processing

Messages dispatched by the Looper are candy by the Handler on the consumer thread. The message type determines the processing:

Task messages
Chore messages contain only a Runnable and no data. Hence, the processing to be executed is defined in the run method of the Runnable, which is executed automatically on the consumer thread, without invoking Handler.handleMessage().
Data messages
When the bulletin contains data, the Handler is the receiver of the information and is responsible for its processing. The consumer thread processes the data by overriding the Handler.handleMessage(Bulletin msg) method. There are two means to exercise this, described in the text that follows.

1 style to define handleMessage is to do it every bit part of creating a Handler. The method should be defined every bit soon as the message queue is bachelor (after Looper.set up() is called) but before the message retrieval starts (before Looper.loop() is called).

A template follows for setting upwardly the handling of data letters:

                class                ConsumerThread                extends                Thread                {                Handler                mHandler                ;                @Override                public                void                run                ()                {                Looper                .                prepare                ();                mHandler                =                new                Handler                ()                {                public                void                handleMessage                (                Bulletin                msg                )                {                // Process data bulletin here                }                };)                Looper                .                loop                ();                }                }              

In this code, the Handler is divers as an anonymous inner class, but information technology could as well have been defined every bit a regular or inner class.

A convenient alternative to extending the Handler course is to use the Handler.Callback interface, which defines a handleMessage method with an boosted return parameter not in Handler.handleMessage():

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

With the Callback interface, it is not necessary to extend the Handler course. Instead, the Callback implementation can be passed to the Handler constructor, and information technology will and then receive the dispatched letters for processing:

                public                grade                HandlerCallbackActivity                extends                Activity                implements                Handler                .                Callback                {                Handler                mUiHandler                ;                @Override                public                void                onCreate                (                Package                savedInstanceState                )                {                super                .                onCreate                (                savedInstanceState                );                mUiHandler                =                new                Handler                (                this                );                }                @Override                public                boolean                handleMessage                (                Message                message                )                {                // Process messages                return                true                ;                }                }              

Callback.handleMessage should return true if the message is handled, which guarantees that no further processing of the message is done. If, however, faux is returned, the bulletin is passed on to the Handler.handleMessage method for farther processing. Note that the Callback does not override Handler.handleMessage. Instead, information technology adds a message preprocessor that is invoked before the Handlers ain method. The Callback preprocessor can intercept and change letters before the Handler receives them. The following lawmaking shows the principle for intercepting messages with the Callback:

                public                class                HandlerCallbackActivity                extends                Activeness                implements                Handler                .                Callback                {                1                @Override                public                boolean                handleMessage                (                Message                msg                )                {                2                switch                (                msg                .                what                )                {                instance                1                :                msg                .                what                =                11                ;                return                truthful                ;                default                :                msg                .                what                =                22                ;                return                fake                ;                }                }                // Invoked on push click                public                void                onHandlerCallback                (                View                v                )                {                Handler                handler                =                new                Handler                (                this                )                {                @Override                public                void                handleMessage                (                Message                msg                )                {                // Process message                                3                }                };                handler                .                sendEmptyMessage                (                1                );                4                handler                .                sendEmptyMessage                (                2                );                5                }                }              
1

The HandlerCallbackActivity implements the Callback interface to intercept letters.

2

The Callback implementation intercepts messages. If msg.what is i, information technology returns true—the bulletin is handled. Otherwise, it changes the value of msg.what to 22 and returns fake—the message is not handled, so it is passed on to the Handler implementation of handleMessage.

3

Process letters in the second Handler.

4

Insert a bulletin with msg.what == 1. The message is intercepted by the Callback as it returns true.

5

Insert a bulletin with msg.what == ii. The bulletin is inverse past the Callback and passed on to the Handler that prints Secondary Handler - msg = 22.

Removing Messages from the Queue

After enqueuing a message, the producer can invoke a method of the Handler course to remove the message, so long as it has not been dequeued by the Looper. Sometimes an application may desire to make clean the message queue by removing all messages, which is possible, but near often a more than fine-grained arroyo is desired: an application wants to target but a subset of the letters. For that, it needs to be able to identify the correct messages. Therefore, letters tin exist identified from certain properties, as shown in Table iv-4.

Tabular array 4-iv. Message identifiers

Identifier type Description Letters to which information technology applies

Handler

Message receiver

Both job and data messages

Object

Message tag

Both task and data messages

Integer

what parameter of message

Data messages

Runnable

Chore to exist executed

Task letters

The handler identifier is mandatory for every message, because a message always knows what Handler it will be dispatched to. This requirement implicitly restricts each Handler to removing merely messages belonging to that Handler. It is not possible for a Handler to remove messages in the queue that were inserted by another Handler.

The methods available in the Handler class for managing the message queue are:

  • Remove a chore from the message: queue.

                        removeCallbacks                    (                    Runnable                    r                    )                    removeCallbacks                    (                    Runnable                    r                    ,                    Object                    token                    )                  
  • Remove a information bulletin from the message queue:

                        removeMessages                    (                    int                    what                    )                    removeMessages                    (                    int                    what                    ,                    Object                    object                    )                  
  • Remove tasks and data letters from the message queue:

                        removeCallbacksAndMessages                    (                    Object                    token                    )                  

The Object identifier is used in both the data and task bulletin. Hence, it can be assigned to messages as a kind of tag, allowing you later to remove related messages that you accept tagged with the aforementioned Object.

For example, the post-obit excerpt inserts ii letters in the queue to make it possible to remove them later based on the tag:

              Object              tag              =              new              Object              ();              1              Handler              handler              =              new              Handler              ()              public              void              handleMessage              (              Message              msg              )              {              // Process message              Log              .              d              (              "Instance"              ,              "Processing message"              );              }              };              Message              bulletin              =              handler              .              obtainMessage              (              0              ,              tag              );              2              handler              .              sendMessage              (              message              );              handler              .              postAtTime              (              new              Runnable              ()              {              3              public              void              run              ()              {              // Left empty for brevity              }              },              tag              ,              SystemClock              .              uptimeMillis              ());              handler              .              removeCallbacksAndMessages              (              tag              );              4            
1

The message tag identifier, common to both the task and data bulletin.

2

The object in a Message instance is used both every bit data container and implicitly defined message tag.

3

Post a chore message with an explicitly divers message tag.

4

Remove all messages with the tag.

Every bit indicated earlier, you take no way to notice out whether a bulletin was dispatched and handled before you lot issue a telephone call to remove it. In one case the message is dispatched, the producer thread that enqueued information technology cannot terminate its job from executing or its data from being processed.

Observing the Bulletin Queue

Information technology is possible to detect pending messages and the dispatching of messages from a Looper to the associated handlers. The Android platform offers two observing mechanisms. Let us take a await at them past example.

The commencement example shows how it is possible to log the current snapshot of awaiting messages in the queue.

Taking a snapshot of the current bulletin queue

This example creates a worker thread when the Activity is created. When the user presses a button, causing onClick to exist called, half dozen messages are added to the queue in different ways. Later on we observe the state of the bulletin queue:

                public                course                MQDebugActivity                extends                Activity                {                private                static                last                Cord                TAG                =                "EAT"                ;                Handler                mWorkerHandler                ;                public                void                onCreate                (                Bundle                savedInstanceState                )                {                super                .                onCreate                (                savedInstanceState                );                setContentView                (                R                .                layout                .                activity_mqdebug                );                Thread                t                =                new                Thread                ()                {                @Override                public                void                run                ()                {                Looper                .                prepare                ();                mWorkerHandler                =                new                Handler                ()                {                @Override                public                void                handleMessage                (                Message                msg                )                {                Log                .                d                (                TAG                ,                "handleMessage - what = "                +                msg                .                what                );                }                };                Looper                .                loop                ();                }                };                t                .                commencement                ();                }                // Chosen on push click, i.eastward. from the UI thread.                public                void                onClick                (                View                5                )                {                mWorkerHandler                .                sendEmptyMessageDelayed                (                1                ,                2000                );                mWorkerHandler                .                sendEmptyMessage                (                two                );                mWorkerHandler                .                obtainMessage                (                3                ,                0                ,                0                ,                new                Object                ()).                sendToTarget                ();                mWorkerHandler                .                sendEmptyMessageDelayed                (                4                ,                300                );                mWorkerHandler                .                postDelayed                (                new                Runnable                ()                {                @Override                public                void                run                ()                {                Log                .                d                (                TAG                ,                "Execute"                );                }                },                400                );                mWorkerHandler                .                sendEmptyMessage                (                v                );                mWorkerHandler                .                dump                (                new                LogPrinter                (                Log                .                DEBUG                ,                TAG                ),                ""                );                }                }              

Six messages, with the parameters shown in Figure 4-10, are added to the queue.

Message queue example.

Figure 4-10. Added messages in the queue

Right afterward the messages are added to the queue, a snapshot is printed to the log. Only awaiting messages are observed. Hence, the number of messages actually observed depends on how many messages accept already been dispatched to the handler. Three of the letters are added without a delay, which makes them eligible for acceleration at the time of the snapshot.

A typical run of the preceding code produces the following log:

49.397: handleMessage - what = 2 49.397: handleMessage - what = 3 49.397: handleMessage - what = five 49.397: Handler (com.eat.MQDebugActivity$1$1) {412cb3d8} @ 5994288 49.407: Looper{412cb070} 49.407:     mRun=true 49.407:     mThread=Thread[Thread-111,v,main] 49.407:     mQueue=android.bone.MessageQueue@412cb090 49.407:         Message 0: { what=4 when=+293ms } 49.407:         Bulletin one: { what=0 when=+394ms } 49.407:         Message 2: { what=i when=+1s990ms } 49.407:         (Total messages: 3) 49.707: handleMessage - what = 4 49.808: Execute 51.407: handleMessage - what = one

The snapshot of the message queue shows that the messages with what parameters (0, 1, and 4) are pending in the queue. These are the messages added to the queue with a dispatch delay, whereas the others without a dispatch delay apparently have been dispatched already. This is a reasonable effect because the handler processing is very short—only a print to the log.

The snapshot also shows how much time is left earlier each bulletin in the queue volition pass the acceleration barrier. For example, the next message to pass the barrier is Message 0 (what= 4) in 293 ms. Messages still pending in the queue only eligible for dispatch volition have a negative fourth dimension indication in the log—e.k., if when is less than aught.

Tracing the bulletin queue processing

The message processing information can exist printed to the log. Message queue logging is enabled from the Looper class. The following telephone call enables logging on the message queue of the calling thread:

                Looper                .                myLooper                ().                setMessageLogging                (                new                LogPrinter                (                Log                .                DEBUG                ,                TAG                ));              

Let's look at an example of tracing a message that is posted to the UI thread:

                mHandler                .                postal service                (                new                Runnable                ()                {                @Override                public                void                run                ()                {                Log                .                d                (                TAG                ,                "Executing Runnable"                );                }                });                mHandler                .                sendEmptyMessage                (                42                );              

The example posts ii events to the message queue: first a Runnable followed by an empty message. As expected, with the sequential execution in mind, the Runnable is processed first, and consequently, is the first to be logged:

>>>>> Dispatching to Handler (android.os.Handler) {4111ef40}       com.consume.MessageTracingActivity$1@41130820: 0 Executing Runnable <<<<< Finished to Handler (android.os.Handler) {4111ef40}       com.eat.MessageTracingActivity$1@41130820

The trace prints the start and end of the event identified by three backdrop:

Handler instance
android.bone.Handler 4111ef40
Task case
com.eat.MessageTracingActivity$i@41130820
The what parameter
0 (Runnable tasks do not conduct a what parameter)

Similarly, the trace of an message with the what parameter prepare to 42 prints the message argument but not any Runnable instance:

>>>>> Dispatching to Handler (android.os.Handler) {4111ef40} null: 42 <<<<< Finished to Handler (android.os.Handler) {4111ef40} nil

Combining the 2 techniques of message queue snapshots and dispatch tracing allows the application to observe bulletin passing in detail.

Communicating with the UI Thread

The UI thread is the only thread in an application that has an associated Looper by default, which is associated on the thread before the first Android component is started. The UI thread tin can be a consumer, to which other threads can laissez passer messages. Information technology's important to transport only short-lived tasks to the UI thread. The UI thread is application global and processes both Android component and system messages sequentially. Hence, long-lived tasks will have a global touch across the application.

Messages are passed to the UI thread through its Looper that is accessible globally in the awarding from all threads with Looper.getMainLooper():

            Runnable            task            =            new            Runnable            ()            {...};            new            Handler            (            Looper            .            getMainLooper            ()).            post            (            task            );          

Independent of the posting thread, the message is inserted in the queue of the UI thread. If it is the UI thread that posts the bulletin to itself, the bulletin tin can be processed at the earliest afterward the electric current message is washed:

            // Method called on UI thread.            private            void            postFromUiThreadToUiThread            ()            {            new            Handler            ().            postal service            (            new            Runnable            ()            {            ...            });            // The code at this point is part of a message being processed            // and is executed before the posted bulletin.            }          

However, a chore message that is posted from the UI thread to itself can featherbed the message passing and execute immediately inside the currently processed bulletin on the UI thread with the convenience method Activeness.runOnUiThread(Runnable):

            // Method called on UI thread.            individual            void            postFromUiThreadToUiThread            ()            {            runOnUiThread            (            new            Runnable            ()            {            ...            });            // The code at this signal is executed later the bulletin.            }          

If it is called outside the UI thread, the message is inserted in the queue. The runOnUiThread method can but exist executed from an Activity instance, merely the same behavior can be implemented past tracking the ID of the UI thread, for example, with a convenience method customRunOnUiThread in an Application subclass. The customRunOnUiThread inserts a bulletin in the queue like the following example:

            public            class            EatApplication            extends            Awarding            {            private            long            mUiThreadId            ;            individual            Handler            mUiHandler            ;            @Override            public            void            onCreate            ()            {            super            .            onCreate            ();            mUiThreadId            =            Thread            .            currentThread            ().            getId            ();            mUiHandler            =            new            Handler            ();            }            public            void            customRunOnUiThread            (            Runnable            activity            )            {            if            (            Thread            .            currentThread            ().            getId            ()            !=            mUiThreadId            )            {            mUiHandler            .            postal service            (            action            );            }            else            {            action            .            run            ();            }            }            }          

Summary

Android applications have access to the regular Java thread advice techniques, which suit worker-thread advice well. However, they rarely fit the utilise case when ane of the threads is the UI thread, which is the near common case. Android message passing is used extensively throughout applications, either explicitly or implicitly, through diverse wrapping techniques that are discussed in the 2nd part of this book.

How To Register Listener In Thread Loop,

Source: https://www.oreilly.com/library/view/efficient-android-threading/9781449364120/ch04.html

Posted by: wagnermouldither.blogspot.com

0 Response to "How To Register Listener In Thread Loop"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel