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).
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.
-
Fix up the connexion:
PipedReaderr=newPipedReader();PipedWriterw=newPipedWriter();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:
intBUFFER_SIZE_IN_CHARS=1024*4;PipedReaderr=newPipedReader(BUFFER_SIZE_IN_CHARS);PipedWriterw=newPipedWriter(r); -
Pass the reader to a processing thread:
Threadt=newMyReaderThread(r);t.start();After the reader thread starts, it is gear up to receive data from the writer.
-
Transfer data:
// Producer thread: Write unmarried character or array of charactersw.write('A');// Consumer thread: Read the dataintresult=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. Theread()method blocks whenever there is no data to read from the pipe. It's worth noticing that theread()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.inti;while((i=reader.read())!=-ane){charc=(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, thePipedReaderuses a blocking phone call towait()with 1-second timeout. Hence, if theflush()telephone call is omitted, the consumer thread may delay the reading of data up to 1 2nd. By callingaffluent(), the producer cuts short the look in the consumer thread and allows data processing to go on immediately. -
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:
publicformPipeExampleActivityextendsActivity{privatestaticfinalStringTAG="PipeExampleActivity";individualEditTexteditText;PipedReaderr;PipedWriterw;privateThreadworkerThread;publicvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);r=newPipedReader();w=newPipedWriter();endeavour{w.connect(r);}catch(IOExceptiondue east){e.printStackTrace();}setContentView(R.layout.activity_pipe);editText=(EditText)findViewById(R.id.edit_text);editText.addTextChangedListener(newTextWatcher(){@OverridepublicvoidbeforeTextChanged(CharSequencecharSequence,intstart,intcount,intafter){}@OverridepublicvoidonTextChanged(CharSequencecharSequence,intfirst,intbefore,intcount){attempt{// Only handle addition of charactersif(count>earlier){// Write the last entered character to the pipew.write(charSequence.subSequence(before,count).toString());}}catch(IOExceptione){e.printStackTrace();}}@OverridepublicvoidafterTextChanged(Editableeditable){}});workerThread=newThread(newTextHandlerTask(r));workerThread.start();}@OverrideprotectedvoidonDestroy(){super.onDestroy();workerThread.interrupt();try{r.shut();w.close();}catch(IOExceptione){}}individualstaticclassTextHandlerTaskimplementsRunnable{privateconcludingPipedReaderreader;publicTextHandlerTask(PipedReaderreader){this.reader=reader;}@Overridepublicvoidrun(){while(Thread.currentThread().isInterrupted()){effort{inti;while((i=reader.read())!=-1){charc=(char)i;//Add together TEXT PROCESSING LOGIC HERELog.d(TAG,"char = "+c);}}catch(IOExceptione){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.
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).
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:
publicclassConsumerProducer{individualfinalintLIMIT=10;privateBlockingQueue<Integer>blockingQueue=newLinkedBlockingQueue<Integer>(LIMIT);publicvoidproduce()throwsInterruptedException{intvalue=0;while(true){blockingQueue.put(value++);}}publicvoidconsume()throwsInterruptedException{while(truthful){intvalue=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.
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
Loopertin 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—andThread—has at nigh iMessageQueue. -
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.
- Insert: The producer thread inserts messages in the queue past using the
Handlercontinued to the consumer thread, every bit shown in Handler. - Recollect: The
Looper, discussed in Looper, runs in the consumer thread and retrieves letters from the queue in a sequential social club. - Acceleration: The handlers are responsible for processing the letters on the consumer thread. A thread may take multiple
Handlerinstances for processing letters; theLooperensures that messages are dispatched to the rightHandler.
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:
publicclassLooperActivityextendsAction{LooperThreadmLooperThread;privatestaticgradeLooperThreadextendsThread{![]()
publicHandlermHandler;publicvoidrun(){Looper.prepare();![]()
mHandler=newHandler(){![]()
publicvoidhandleMessage(Messagemsg){![]()
if(msg.what==0){doLongRunningOperation();}}};Looper.loop();![]()
}}publicvoidonCreate(PacketsavedInstanceState){super.onCreate(savedInstanceState);mLooperThread=newLooperThread();![]()
mLooperThread.kickoff();}publicvoidonClick(Viewfive){if(mLooperThread.mHandler!=null){![]()
Bulletinmsg=mLooperThread.mHandler.obtainMessage(0);![]()
mLooperThread.mHandler.sendMessage(msg);![]()
}}privatevoiddoLongRunningOperation(){// Add long running operation here.}protectedvoidonDestroy(){mLooperThread.mHandler.getLooper().quit();![]()
}}
-
-
Definition of the worker thread, acting equally a consumer of the message queue.
-
-
Acquaintance a
Looper—and implicitly aMessageQueue—with the thread. -
-
Ready a
Handlerto be used by the producer for inserting messages in the queue. Here we employ the default constructor so it volition bind to theLooperof the current thread. Hence, thisHandlertin can created only afterwardsLooper.set up(), or it will have nothing to bind to. -
-
Callback that runs when the bulletin has been dispatched to the worker thread. It checks the
whatparameter and then executes the long functioning. -
-
First dispatching messages from the message queue to the consumer thread. This is a blocking call, then the worker thread volition non finish.
-
-
Start the worker thread, so that it is ready to process messages.
-
-
There is race status between the setup of
mHandleron a background thread and this usage on the UI thread. Hence, validate thatmHandleris available. -
-
Initialize a
Message-object with thewhatargument arbitrarily set to 0. -
-
Insert the message in the queue.
-
-
Cease the background thread. The call to
Looper.quit()stops the dispatching of messages and releasesLooper.loop()from blocking and so therunmethod 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).
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.
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.MessageQueuemq=Looper.myQueue();// Create and annals an idle listener.MessageQueue.IdleHandleridleHandler=newMessageQueue.IdleHandler();mq.addIdleHandler(idleHandler)// Unregister an idle listener.mq.removeIdleHandler(idleHandler)
The idle handler interface consists of one callback method only:
interfaceIdleHandler{booleanqueueIdle();}
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:
publicgradeConsumeAndQuitThreadextendsThreadimplementsMessageQueue.IdleHandler{individualstaticfinalStringTHREAD_NAME="ConsumeAndQuitThread";publicHandlermConsumerHandler;privatebooleanmIsFirstIdle=truthful;publicConsumeAndQuitThread(){super(THREAD_NAME);}@Overridepublicvoidrun(){Looper.set();mConsumerHandler=newHandler(){@OverridepublicvoidhandleMessage(Messagemsg){// Swallow information}};Looper.myQueue().addIdleHandler(this);![]()
Looper.loop();}@OverridepublicbooleanqueueIdle(){if(mIsFirstIdle){![]()
mIsFirstIdle=false;returntrue;![]()
}mConsumerHandler.getLooper().quit();![]()
returnfalse;}publicvoidenqueueData(inti){mConsumerHandler.sendEmptyMessage(i);}}
-
-
Register the
IdleHandleron the groundwork thread when it is started and theLooperis prepared so that theMessageQueueis prepare up. -
-
Let the first
queueIdleinvocation pass, since it occurs before the first message is received. -
-
Render
truthfulon the first invocation so that theIdleHandlerstill is registered. -
-
Terminate the thread.
The bulletin insertion is done from multiple threads concurrently, with a false randomness of the insertion time:
concludingConsumeAndQuitThreadconsumeAndQuitThread=newConsumeAndQuitThread();consumeAndQuitThread.start();for(inti=0;i<10;i++){newThread(newRunnable(){@Overridepublicvoidrun(){for(inti=0;i<10;i++){SystemClock.slumber(newRandom().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():
Messagem=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 whatintMessage identifier. Communicates intention of the bulletin.
arg1,arg2intSimple 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 thedataparameter.objObjectArbitrary object. If the object is handed off to a thread in another process, it has to implement
Parcelable.dataParcelContainer of arbitrary data values.
replyToMessengerReference to
Handlerin some other procedure. Enables interprocess bulletin communication, as described in Two-Style Communication.callbackRunnableTask to execute on a thread. This is an internal case field that holds the Runnable object from the
Handler.postmethods in Handler. - Job message
- The task is represented by a
coffee.lang.Runnableobject 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.
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:
Messagem=newMessage(); -
Factory methods:
-
Empty message:
Messagem=Message.obtain(); -
Data bulletin:
Messagem=Message.obtain(Handlerh);Messageone thousand=Message.obtain(Handlerh,intwhat);Bulletin1000=Bulletin.obtain(Handlerh,intwhat,Objecto);Messagem=Bulletin.obtain(Handlerh,intwhat,intarg1,intarg2);Messagem=Message.obtain(Handlerh,intwhat,intarg1,intarg2,Objecto); -
Task message:
Messagem=Message.obtain(Handlerh,Runnabletask); -
Re-create constructor:
Messagem=Message.obtain(MessageoriginalMsg);
-
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:
classConsumerThreadextendsThread{@Overridepublicvoidrun(){Looper.fix();![]()
// Handler cosmos omitted.Looper.loop();![]()
}}
-
-
The first stride is to create the
Looper, which is done with the staticprepare()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. -
-
Start treatment messages in the bulletin queue. This is a blocking method that ensures the
run()method is not finished; whilerun()blocks, theLooperdispatches 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()throwsRuntimeException. - The runtime associates a
Looperto the UI thread byLooper.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
Looperbind to theLooperof the electric current thread:newHandler();newHandler(Handler.Callback) -
Constructors with an explicit
Looperbind to thatLooper:newHandler(Looper);newHandler(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.
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:
MessageobtainMessage(intwhat,intarg1,intarg2)MessageobtainMessage()BulletinobtainMessage(intwhat,intarg1,intarg2,Objectobj)BulletinobtainMessage(intwhat)BulletinobtainMessage(intwhat,Objectobj)
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:
booleanmail(Runnabler)fbooleanpostAtFrontOfQueue(Runnabler)booleanpostAtTime(Runnabler,Objecttoken,longuptimeMillis)booleanpostAtTime(Runnabler,longuptimeMillis)booleanpostDelayed(Runnabler,longdelayMillis) -
Add together a data object to the message queue:
booleansendMessage(Messagemsg)booleansendMessageAtFrontOfQueue(Bulletinmsg)booleansendMessageAtTime(Bulletinmsg,longuptimeMillis)booleansendMessageDelayed(Messagemsg,longdelayMillis) -
Add together simple information object to the message queue:
booleansendEmptyMessage(intwhat)booleansendEmptyMessageAtTime(intwhat,longuptimeMillis)booleansendEmptyMessageDelayed(intwhat,longdelayMillis)
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 | | Message was created from a |
| Message has already been dispatched and is being processed. | | The same message instance was inserted twice. |
| Looper has exited. | | Bulletin is inserted afterwards |
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:
publicclassHandlerExampleActivityextendsAction{privatefinalstaticintSHOW_PROGRESS_BAR=1;privatelaststaticintHIDE_PROGRESS_BAR=0;individualBackgroundThreadmBackgroundThread;privateTextViewmText;privatePush buttonmButton;privateProgressBarmProgressBar;@OverridepublicvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_handler_example);mBackgroundThread=newBackgroundThread();mBackgroundThread.start();![]()
mText=(TextView)findViewById(R.id.text);mProgressBar=(ProgressBar)findViewById(R.id.progress);mButton=(Push button)findViewById(R.id.push button);mButton.setOnClickListener(newOnClickListener(){@OverridepublicvoidonClick(View5){mBackgroundThread.doWork();![]()
}});}@OverrideprotectedvoidonDestroy(){super.onDestroy();mBackgroundThread.exit();![]()
}// ... The balance of the Activity is defined further downwardly}
-
-
A background thread with a message queue is started when the
HandlerExampleActivityis created. It handles tasks from the UI thread. -
-
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.
-
-
The background thread is stopped when the
HandlerExampleActivityis 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:
individualclassBackgroundThreadextendsThread{privateHandlermBackgroundHandler;publicvoidrun(){![]()
Looper.prepare();mBackgroundHandler=newHandler();![]()
Looper.loop();}publicvoiddoWork(){mBackgroundHandler.post(newRunnable(){![]()
@Overridepublicvoidrun(){MessageuiMsg=mUiHandler.obtainMessage(SHOW_PROGRESS_BAR,0,0,null);![]()
mUiHandler.sendMessage(uiMsg);![]()
Randomr=newRandom();intrandomInt=r.nextInt(5000);SystemClock.slumber(randomInt);![]()
uiMsg=mUiHandler.obtainMessage(HIDE_PROGRESS_BAR,randomInt,0,zip);![]()
mUiHandler.sendMessage(uiMsg);![]()
}});}publicvoidleave(){![]()
mBackgroundHandler.getLooper().quit();}}
-
-
Associate a
Looperwith the thread. -
-
The
Handlerprocesses simplyRunnables. Hence, it is non required to implementHandler.handleMessage. -
-
Post a long task to be executed in the background.
-
-
Create a
Messageobject that contains only awhatstatement with a command—SHOW_PROGRESS_BAR—to the UI thread so that it tin can testify the progress bar. -
-
Send the start message to the UI thread.
-
-
Simulate a long task of random length, that produces some data
randomInt. -
-
Create a
Bulletinobject with the issuerandomInt, that is passed in thearg1parameter. Thewhatparameter contains a command—HIDE_PROGRESS_BAR—to remove the progress bar. -
-
The message with the finish issue that both informs the UI thread that the task is finished and delivers a result.
-
-
Quit the
Looperand 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:
privatefinalHandlermUiHandler=newHandler(){publicvoidhandleMessage(Messagemsg){switch(msg.what){caseSHOW_PROGRESS_BAR:![]()
mProgressBar.setVisibility(View.VISIBLE);break;caseHIDE_PROGRESS_BAR:![]()
mText.setText(String.valueOf(msg.arg1));mProgressBar.setVisibility(View.INVISIBLE);intermission;}}};
-
-
Show the progress bar.
-
-
Hide the progress bar and update the
TextViewwith 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
Runnableand no data. Hence, the processing to be executed is defined in therunmethod of theRunnable, which is executed automatically on the consumer thread, without invokingHandler.handleMessage(). - Data messages
- When the bulletin contains data, the
Handleris the receiver of the information and is responsible for its processing. The consumer thread processes the data by overriding theHandler.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:
classConsumerThreadextendsThread{HandlermHandler;@Overridepublicvoidrun(){Looper.prepare();mHandler=newHandler(){publicvoidhandleMessage(Bulletinmsg){// 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():
publicinterfaceCallback{publicbooleanhandleMessage(Bulletinmsg);}
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:
publicgradeHandlerCallbackActivityextendsActivityimplementsHandler.Callback{HandlermUiHandler;@OverridepublicvoidonCreate(PackagesavedInstanceState){super.onCreate(savedInstanceState);mUiHandler=newHandler(this);}@OverridepublicbooleanhandleMessage(Messagemessage){// Process messagesreturntrue;}}
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:
publicclassHandlerCallbackActivityextendsActivenessimplementsHandler.Callback{![]()
@OverridepublicbooleanhandleMessage(Messagemsg){![]()
switch(msg.what){instance1:msg.what=11;returntruthful;default:msg.what=22;returnfake;}}// Invoked on push clickpublicvoidonHandlerCallback(Viewv){Handlerhandler=newHandler(this){@OverridepublicvoidhandleMessage(Messagemsg){// Process message![]()
}};handler.sendEmptyMessage(1);![]()
handler.sendEmptyMessage(2);![]()
}}
-
-
The
HandlerCallbackActivityimplements theCallbackinterface to intercept letters. -
-
The
Callbackimplementation intercepts messages. Ifmsg.whatis i, information technology returnstrue—the bulletin is handled. Otherwise, it changes the value ofmsg.whatto 22 and returnsfake—the message is not handled, so it is passed on to theHandlerimplementation ofhandleMessage. -
-
Process letters in the second
Handler. -
-
Insert a bulletin with
msg.what == 1. The message is intercepted by theCallbackas it returnstrue. -
-
Insert a bulletin with
msg.what == ii. The bulletin is inverse past theCallbackand passed on to theHandlerthat printsSecondary 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 | | 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(Runnabler)removeCallbacks(Runnabler,Objecttoken) -
Remove a information bulletin from the message queue:
removeMessages(intwhat)removeMessages(intwhat,Objectobject) -
Remove tasks and data letters from the message queue:
removeCallbacksAndMessages(Objecttoken)
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:
Objecttag=newObject();![]()
Handlerhandler=newHandler()publicvoidhandleMessage(Messagemsg){// Process messageLog.d("Instance","Processing message");}};Messagebulletin=handler.obtainMessage(0,tag);![]()
handler.sendMessage(message);handler.postAtTime(newRunnable(){![]()
publicvoidrun(){// Left empty for brevity}},tag,SystemClock.uptimeMillis());handler.removeCallbacksAndMessages(tag);![]()
-
-
The message tag identifier, common to both the task and data bulletin.
-
-
The object in a
Messageinstance is used both every bit data container and implicitly defined message tag. -
-
Post a chore message with an explicitly divers message tag.
-
-
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:
publiccourseMQDebugActivityextendsActivity{privatestaticlastCordTAG="EAT";HandlermWorkerHandler;publicvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_mqdebug);Threadt=newThread(){@Overridepublicvoidrun(){Looper.prepare();mWorkerHandler=newHandler(){@OverridepublicvoidhandleMessage(Messagemsg){Log.d(TAG,"handleMessage - what = "+msg.what);}};Looper.loop();}};t.commencement();}// Chosen on push click, i.eastward. from the UI thread.publicvoidonClick(View5){mWorkerHandler.sendEmptyMessageDelayed(1,2000);mWorkerHandler.sendEmptyMessage(two);mWorkerHandler.obtainMessage(3,0,0,newObject()).sendToTarget();mWorkerHandler.sendEmptyMessageDelayed(4,300);mWorkerHandler.postDelayed(newRunnable(){@Overridepublicvoidrun(){Log.d(TAG,"Execute");}},400);mWorkerHandler.sendEmptyMessage(v);mWorkerHandler.dump(newLogPrinter(Log.DEBUG,TAG),"");}}
Six messages, with the parameters shown in Figure 4-10, are added to the queue.
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(newLogPrinter(Log.DEBUG,TAG));
Let's look at an example of tracing a message that is posted to the UI thread:
mHandler.postal service(newRunnable(){@Overridepublicvoidrun(){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.Handler4111ef40 - Task case
-
com.eat.MessageTracingActivity$i@41130820 - The
whatparameter - 0 (
Runnabletasks do not conduct awhatparameter)
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():
Runnabletask=newRunnable(){...};newHandler(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.privatevoidpostFromUiThreadToUiThread(){newHandler().postal service(newRunnable(){...});// 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.individualvoidpostFromUiThreadToUiThread(){runOnUiThread(newRunnable(){...});// 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:
publicclassEatApplicationextendsAwarding{privatelongmUiThreadId;individualHandlermUiHandler;@OverridepublicvoidonCreate(){super.onCreate();mUiThreadId=Thread.currentThread().getId();mUiHandler=newHandler();}publicvoidcustomRunOnUiThread(Runnableactivity){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