2022-10-02 23:06:56 +02:00
/ *
* Copyright ( C ) 2006 The Android Open Source Project
*
* Licensed under the Apache License , Version 2 . 0 ( the " License " ) ;
* you may not use this file except in compliance with the License .
* You may obtain a copy of the License at
*
* http : //www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing , software
* distributed under the License is distributed on an " AS IS " BASIS ,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
* See the License for the specific language governing permissions and
* limitations under the License .
* /
package android.os ;
import android.util.Log ;
import android.util.PrefixPrinter ;
2023-06-22 11:45:46 +02:00
import android.util.Printer ;
2022-10-02 23:06:56 +02:00
/ * *
2023-06-22 11:45:46 +02:00
* Class used to run a message loop for a thread . Threads by default do
* not have a message loop associated with them ; to create one , call
* { @link # prepare } in the thread that is to run the loop , and then
* { @link # loop } to have it process messages until the loop is stopped .
*
* < p > Most interaction with a message loop is through the
* { @link Handler } class .
*
* < p > This is a typical example of the implementation of a Looper thread ,
* using the separation of { @link # prepare } and { @link # loop } to create an
* initial Handler to communicate with the Looper .
*
* < pre >
* class LooperThread extends Thread {
* public Handler mHandler ;
*
* public void run ( ) {
* Looper . prepare ( ) ;
*
* mHandler = new Handler ( ) {
* public void handleMessage ( Message msg ) {
* // process incoming messages here
* }
* } ;
*
* Looper . loop ( ) ;
* }
* } < / pre >
* /
2022-10-02 23:06:56 +02:00
public final class Looper {
2023-06-22 11:45:46 +02:00
private static final String TAG = " Looper " ;
2022-10-02 23:06:56 +02:00
2023-06-22 11:45:46 +02:00
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal < Looper > sThreadLocal = new ThreadLocal < Looper > ( ) ;
private static Looper sMainLooper ; // guarded by Looper.class
final MessageQueue mQueue ;
final Thread mThread ;
private Printer mLogging ;
/ * *
* Initialize the current thread as a looper .
* This gives you a chance to create handlers that then reference
* this looper , before actually starting the loop . Be sure to call
* { @link # loop ( ) } after calling this method , and end it by calling
* { @link # quit ( ) } .
* /
public static void prepare ( ) {
prepare ( true ) ;
}
private static void prepare ( boolean quitAllowed ) {
if ( sThreadLocal . get ( ) ! = null ) {
throw new RuntimeException ( " Only one Looper may be created per thread " ) ;
}
2023-07-25 14:26:29 +02:00
sThreadLocal . set ( new Looper ( quitAllowed ) ) ;
2023-06-22 11:45:46 +02:00
}
/ * *
* Initialize the current thread as a looper , marking it as an
* application ' s main looper . The main looper for your application
* is created by the Android environment , so you should never need
* to call this function yourself . See also : { @link # prepare ( ) }
* /
public static void prepareMainLooper ( ) {
prepare ( false ) ;
synchronized ( Looper . class ) {
if ( sMainLooper ! = null ) {
throw new IllegalStateException ( " The main Looper has already been prepared. " ) ;
}
sMainLooper = myLooper ( ) ;
}
}
/ * *
* Returns the application ' s main looper , which lives in the main thread of the application .
* /
public static Looper getMainLooper ( ) {
2023-07-25 14:26:29 +02:00
synchronized ( Looper . class ) {
return sMainLooper ;
}
2023-06-22 11:45:46 +02:00
}
/ * *
* Run the message queue in this thread . Be sure to call
* { @link # quit ( ) } to end the loop .
* /
public static void loop ( ) {
2023-07-25 14:26:29 +02:00
final Looper me = myLooper ( ) ;
2023-06-22 11:45:46 +02:00
if ( me = = null ) {
2023-07-25 14:26:29 +02:00
throw new RuntimeException ( " No Looper; Looper.prepare() wasn't called on this thread. " ) ;
2022-10-02 23:06:56 +02:00
}
2023-06-22 11:45:46 +02:00
final MessageQueue queue = me . mQueue ;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
2023-07-25 14:26:29 +02:00
// Binder.clearCallingIdentity();
// final long ident = Binder.clearCallingIdentity();
2023-06-22 11:45:46 +02:00
for ( ; ; ) {
2023-07-25 14:26:29 +02:00
Message msg = queue . next ( ) ; // might block
if ( msg = = null ) {
// No message indicates that the message queue is quitting.
return ;
}
2023-06-22 11:45:46 +02:00
2023-07-25 14:26:29 +02:00
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me . mLogging ;
if ( logging ! = null ) {
logging . println ( " >>>>> Dispatching to " + msg . target + " " +
msg . callback + " : " + msg . what ) ;
}
2023-06-22 11:45:46 +02:00
2023-07-25 14:26:29 +02:00
msg . target . dispatchMessage ( msg ) ;
2023-06-22 11:45:46 +02:00
2023-07-25 14:26:29 +02:00
if ( logging ! = null ) {
logging . println ( " <<<<< Finished to " + msg . target + " " + msg . callback ) ;
}
2023-06-22 11:45:46 +02:00
2023-07-25 14:26:29 +02:00
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
/ * final long newIdent = Binder . clearCallingIdentity ( ) ;
if ( ident ! = newIdent ) {
Log . wtf ( TAG , " Thread identity changed from 0x " + Long . toHexString ( ident ) + " to 0x " + Long . toHexString ( newIdent ) + " while dispatching to " + msg . target . getClass ( ) . getName ( ) + " " + msg . callback + " what= " + msg . what ) ;
}
* /
msg . recycle ( ) ;
}
2023-06-22 11:45:46 +02:00
}
/ * *
* Return the Looper object associated with the current thread . Returns
* null if the calling thread is not associated with a Looper .
* /
public static Looper myLooper ( ) {
2023-07-25 14:26:29 +02:00
return sThreadLocal . get ( ) ;
2023-06-22 11:45:46 +02:00
}
/ * *
* Control logging of messages as they are processed by this Looper . If
* enabled , a log message will be written to < var > printer < / var >
* at the beginning and ending of each message dispatch , identifying the
* target Handler and message contents .
*
* @param printer A Printer object that will receive log messages , or
* null to disable message logging .
* /
public void setMessageLogging ( Printer printer ) {
mLogging = printer ;
}
/ * *
* Return the { @link MessageQueue } object associated with the current
* thread . This must be called from a thread running a Looper , or a
* NullPointerException will be thrown .
* /
public static MessageQueue myQueue ( ) {
return myLooper ( ) . mQueue ;
}
private Looper ( boolean quitAllowed ) {
mQueue = new MessageQueue ( quitAllowed ) ;
mThread = Thread . currentThread ( ) ;
}
/ * *
* Returns true if the current thread is this looper ' s thread .
* @hide
* /
public boolean isCurrentThread ( ) {
return Thread . currentThread ( ) = = mThread ;
}
/ * *
* Quits the looper .
* < p >
* Causes the { @link # loop } method to terminate without processing any
* more messages in the message queue .
* < / p > < p >
* Any attempt to post messages to the queue after the looper is asked to quit will fail .
* For example , the { @link Handler # sendMessage ( Message ) } method will return false .
* < / p > < p class = " note " >
* Using this method may be unsafe because some messages may not be delivered
* before the looper terminates . Consider using { @link # quitSafely } instead to ensure
* that all pending work is completed in an orderly manner .
* < / p >
*
* @see # quitSafely
* /
public void quit ( ) {
mQueue . quit ( false ) ;
}
/ * *
* Quits the looper safely .
* < p >
* Causes the { @link # loop } method to terminate as soon as all remaining messages
* in the message queue that are already due to be delivered have been handled .
* However pending delayed messages with due times in the future will not be
* delivered before the loop terminates .
* < / p > < p >
* Any attempt to post messages to the queue after the looper is asked to quit will fail .
* For example , the { @link Handler # sendMessage ( Message ) } method will return false .
* < / p >
* /
public void quitSafely ( ) {
mQueue . quit ( true ) ;
}
/ * *
* Posts a synchronization barrier to the Looper ' s message queue .
*
* Message processing occurs as usual until the message queue encounters the
* synchronization barrier that has been posted . When the barrier is encountered ,
* later synchronous messages in the queue are stalled ( prevented from being executed )
* until the barrier is released by calling { @link # removeSyncBarrier } and specifying
* the token that identifies the synchronization barrier .
*
* This method is used to immediately postpone execution of all subsequently posted
* synchronous messages until a condition is met that releases the barrier .
* Asynchronous messages ( see { @link Message # isAsynchronous } are exempt from the barrier
* and continue to be processed as usual .
*
* This call must be always matched by a call to { @link # removeSyncBarrier } with
* the same token to ensure that the message queue resumes normal operation .
* Otherwise the application will probably hang !
*
* @return A token that uniquely identifies the barrier . This token must be
* passed to { @link # removeSyncBarrier } to release the barrier .
*
* @hide
* /
public int postSyncBarrier ( ) {
return mQueue . enqueueSyncBarrier ( SystemClock . uptimeMillis ( ) ) ;
}
/ * *
* Removes a synchronization barrier .
*
* @param token The synchronization barrier token that was returned by
* { @link # postSyncBarrier } .
*
* @throws IllegalStateException if the barrier was not found .
*
* @hide
* /
public void removeSyncBarrier ( int token ) {
mQueue . removeSyncBarrier ( token ) ;
}
/ * *
* Return the Thread associated with this Looper .
* /
public Thread getThread ( ) {
2023-07-25 14:26:29 +02:00
return mThread ;
2023-06-22 11:45:46 +02:00
}
/ * *
* @hide
* /
public MessageQueue getQueue ( ) {
return mQueue ;
}
/ * *
* Return whether this looper ' s thread is currently idle , waiting for new work
* to do . This is intrinsically racy , since its state can change before you get
* the result back .
* @hide
* /
public boolean isIdling ( ) {
return mQueue . isIdling ( ) ;
}
public void dump ( Printer pw , String prefix ) {
pw . println ( prefix + toString ( ) ) ;
mQueue . dump ( pw , prefix + " " ) ;
}
public String toString ( ) {
return " Looper ( " + mThread . getName ( ) + " , tid " + mThread . getId ( ) + " ) { " + Integer . toHexString ( System . identityHashCode ( this ) ) + " } " ;
}
2022-10-02 23:06:56 +02:00
}