SDK 30

Handler

The source code comments

A Handler allows you to send and process Message and Runnable objects associated with a thread’s MessageQueue. Each Handler instance is associated with a single thread and that thread’s message queue. When you create a new Handler it is bound to a Looper. It will deliver messages and runnables to that Looper’s message queue and execute them on that Looper’s thread.

Handler allows you to send and process Message and Runnable objects associated with the thread MessageQueue. Each Handler instance is associated with a thread and its Message queue. But you create a new Handler that binds to a Looper. The Handler sends message and Runnable to the Message Queue bound to Looper and executes message and Runnale on the Looper thread.

There are two main uses for a Handler: (1) to schedule messages and runnables to be executed at some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.

Handlers have two main uses :(1) scheduling message and runnable execution at some point in the future, and (2) enlisting an action that executes outside of your own thread.

Scheduling messages is accomplished with the post(Runnable), postAtTime(java.lang.Runnable, long), postDelayed(Runnable, Object, long), sendEmptyMessage(int), sendMessage(Message), sendMessageAtTime(Message, long), and sendMessageDelayed(Message, long) methods. The post versions allow you to enqueue Runnable objects to be called by the message queue when they are received; the sendMessage versions allow you to enqueue a Message object containing a bundle of data that will be processed by the Handler’s handleMessage(Message) method (requiring that you implement a subclass of Handler).

Dispatch message via POST (Runnable), postAtTime(java.lang.runnable, long), postDelayed(Runnable, Object, long), SendEmptyMessage (int), sendMessage(Message), sendMessageAtTime(Message, Long), and sendMessageDelayed(Message, Long) ‘complete. The POST version allows you to queue Runnable objects and call them when message Queue receives them. The sendMessage version allows you to queue a Message object that contains some data, which is processed in Handler’s handleMessage method (requiring you to implement a Handler subclass).

When posting or sending to a Handler, you can either allow the item to be processed as soon as the message queue is ready to do so, or specify a delay before it gets processed or absolute time for it to be processed. The latter two allow you to implement timeouts, ticks, and other timing-based behavior.

When a Handler post or send is sent, you can either tell the Handler to process it when the Message Queue is ready to do it or specify a delay before it is processed or specify an absolute time for processing. The latter two allow you to implement timeouts, ticks and other time-based behaviors.

When a process is created for your application, its main thread is dedicated to running a message queue that takes care of managing the top-level application objects (activities, broadcast receivers, etc) and any windows they create. You can create your own threads, and communicate back with the main application thread through a Handler. This is done by calling the same post or sendMessage methods as before, but from your new thread. The given Runnable or Message will then be scheduled in the Handler’s message queue and processed when appropriate.

After creating a thread for your application, the TA’s main thread is used to run a Message queue that carefully manages the top-level application objects (Activities, Broadcast Receivers, etc.) and the Windows they create. You can create your own thread that communicates with the main thread of your application via a Handler. Do this by calling the aforementioned POST and sendMessage methods in your new thread. The given Runnable or Message will be scheduled in the Handler’s Message queue and will be processed when appropriate.

Handler#post

Add Runnable to the Message queue. Runnable will run on the thread of Handler attach.

Handler#sendMessage

Handler#sendMessage sets the target of Message to this, binding Message to Handler.

Handler#dispatchMessage

handleCallback(msg) message.callback.run()

mCallback.handleMessage(msg)

HandleMessage (MSG) is an empty method by default, which you need to implement if you want to process MSG.

Looper

The source code comments

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 prepare() in the thread that is to run the loop, and then loop() to have it process messages until the loop is stopped.

The message loop class that runs the thread. By default, threads do not have a message loop associated with them. Call prepare in the thread to run the loop, and call loop() to process message until the loop stops.

Most interaction with a message loop is through the Handler class.

Most of the interaction with Message Loop is through the Handler class.

This is a typical example of the implementation of a Looper thread, using the separation of prepare() and loop() to create an initial Handler to communicate with the Looper.

This is a typical example of a Looper thread implementation, using separate prepare and loop to create an initial Handler to communicate with the Looper.

  class LooperThread extends Thread {
      public Handler mHandler;

      public void run(a) {
          Looper.prepare();

          mHandler = new Handler(Looper.myLooper()) {
              public void handleMessage(Message msg) {
                  // process incoming messages here}}; Looper.loop(); }}Copy the code

A constructor

Initialize MessageQueue in the Looper constructor.

Looper#prepare

Call the constructor, new a Looper. Store Looper in ThreadLocal.

Looper#loop

The Looper is retrieved from ThreadLocal and the MessageQueue is retrieved from the Looper. Try to get the next Message in an infinite loop. If there is a call the MSG. Target. DispatchMessage (MSG) in MSG corresponding Handler thread execution method.

Message

The source code comments

Defines a message containing a description and arbitrary data object that can be sent to a Handler. This object contains two extra int fields and an extra object field that allow you to not do allocations in many cases.

Defines a message containing a description and any data object that can be sent by the Handler. This object contains two additional int fields, and one additional object field, allowing you to unallocate in most cases.

While the constructor of Message is public, the best way to get one of these is to call Message.obtain() or one of the Handler.obtainMessage() methods, which will pull them from a pool of recycled objects.

Although the constructor of a Message is public, the best way to obtain a Message is to call either message.obtain () or handler.obtainMessage (), These methods can get messages from a pool of reused objects.

MessageQueue

The source code comments

Low-level class holding the list of messages to be dispatched by a Looper. Messages are not added directly to a MessageQueue, but rather through Handler objects associated with the Looper.

The underlying class that contains the list of messages distributed by Looper. Messages are not added directly to MessageQueue, but through the Handler object associated with Looper.

You can retrieve the MessageQueue for the current thread with Looper.myQueue().

You can get the MessageQueue of the current thread by calling Looper.myQueue().

ThreadLocal

The source code comments

This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID). For example, the class below generates unique identifiers local to each thread. A thread’s id is assigned the first time it invokes ThreadId.get() and remains unchanged on subsequent calls.

This class provides a thread-local variable. These variables differ from their normal counterparts in that each thread accessing the variable (through its GET or set methods) has its own, independently initialized copy of the variable. ThreadLocal instances are typically private static fields in a class that you want to associate state with a thread (for example, user ID or transaction ID). For example, the following class generates a local unique identifier for each thread. The thread’s ID is assigned the first time it calls threadid.get () and remains the same through subsequent calls.

   import java.util.concurrent.atomic.AtomicInteger;

   public class ThreadId {
       // Atomic integer containing the next thread ID to be assigned
       private static final AtomicInteger nextId = new AtomicInteger(0);

       // Thread local variable containing each thread's ID
       private static final ThreadLocal<Integer> threadId =
           new ThreadLocal<Integer>() {
               @Override protected Integer initialValue(a) {
                   returnnextId.getAndIncrement(); }};// Returns the current thread's unique ID, assigning it if necessary
       public static int get(a) {
           returnthreadId.get(); }}Copy the code

Each thread holds an implicit reference to its copy of a thread-local variable as long as the thread is alive and the ThreadLocal instance is accessible; after a thread goes away, all of its copies of thread-local instances are subject to garbage collection (unless other references to these copies exist).

Each thread holds an implicit reference to a copy of the ThreadLocal variable, as long as the thread is alive and the ThreadLocal instance is accessible. After a thread disappears, all instances of thread-local are garbage collected (unless there are other references to those copies).

Message Mechanism Overview

Looper#prepare creates the corresponding Looper for the thread. Looper is used as an argument to create a Handler object that implements methods that respond to Message. When you need to send a Message, you get the Message object. The Handler then sends the Message and adds the Message to the MessageQueue. Stars know that there are in the process of loop MessageQueue message is sent to, the message will be removed, call MSG. Target. DispatchMessage (MSG) in MSG corresponding Handler thread execution response method of MSG.

Some of the problems

Why doesn’t the main thread’s Looper infinite loop cause the application to freeze?

A thread is a piece of executable code. When the executable code completes, the thread life cycle terminates and the thread exits. However, the main thread can not run for a period of time and then automatically end, so how to ensure that it is always alive? Simple approach is executable code has been implemented, infinite loop can guarantee will not be out, for example: binder thread is also the method of infinite loop, cycle through different ways and binder drive to read and write operations, of course, is not a simple infinite loop, no message will sleep, but infinite loop and how to deal with other things?? By creating a new thread. Looper.loop() itself does not cause the application to freeze if the callback methods onCreate, onStart, onResume and so on take too long.

ActivityThread#main, which creates binder channels (new threads) before loop.

public static void main(String[] args) {
    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");

    // Install selective syscall interception
    AndroidOs.install();

    // CloseGuard defaults to true and can be quite spammy. We
    // disable it here, but selectively enable it later (via
    // StrictMode) on debug builds, but using DropBox, not logs.
    CloseGuard.setEnabled(false);

    Environment.initForCurrentUser();

    // Make sure TrustedCertificateStore looks in the right place for CA certificates
    final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
    TrustedCertificateStore.setDefaultUserDirectory(configDir);

    // Call per-process mainline module initialization.
    initializeMainlineModules();

    Process.setArgV0("<pre-initialized>");

    Looper.prepareMainLooper();

    // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
    // It will be in the format "seq=114"
    long startSeq = 0;
    if(args ! =null) {
        for (int i = args.length - 1; i >= 0; --i) {
            if(args[i] ! =null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                startSeq = Long.parseLong(
                        args[i].substring(PROC_START_SEQ_IDENT.length()));
            }
        }
    }
    ActivityThread thread = new ActivityThread();
    // Create a new thread.
    thread.attach(false, startSeq);

    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }

    if (false) {
        Looper.myLooper().setMessageLogging(new
                LogPrinter(Log.DEBUG, "ActivityThread"));
    }

    // End of event ActivityThreadMain.
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    Looper.loop();

    throw new RuntimeException("Main thread loop unexpectedly exited");
}
Copy the code

Are infinite loops too CPU intensive?

Does an infinite loop running on the main thread cost a lot of CPU? If the main thread MessageQueue has no message, it blocks the nativePollOnce() method in the loop queue.next(). The main thread then releases CPU resources and goes to sleep until the next message arrives or a transaction occurs, waking up the main thread by writing data to the pipe end. The epoll mechanism adopted here is an IO multiplexing mechanism, which can monitor multiple descriptors at the same time. When a descriptor is ready (read or write ready), it immediately notifies the corresponding program to carry out read or write operations, which is essentially synchronous I/O, that is, read and write is blocked. Therefore, the main thread is dormant most of the time and does not consume a lot of CPU resources.

Other threads created by Android besides a few child threads created manually?

We can get the top-level thread group recursively printed like this:

Those familiar with threadgroups know that there are two static member variables in a ThreadGroup: systemThreadGroup and mainThreadGroup. MainThreadGroup is also a subgroup of systemThreadGroup, so we just need to get the systemThreadGroup object by reflection and print it recursively.

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?). {
        super.onCreate(savedInstanceState)
        fun printThreads(threadGroup: ThreadGroup) {
            "group name: ${threadGroup.name}".logI()
            Threads are disabled. @hide is not found in the source code
            // threadGroup::class.get
      
       ? >(threadGroup, "threads")? .filterNotNull()? .forEach { "thread name: ${it.name}".logI() }
      arrayOfNulls<Thread? >(threadGroup.activeCount()).apply { threadGroup.enumerate(this.false) }
                .filterNotNull().forEach { "thread name: ${it.name}".logI() }
            threadGroup::class.get<Array<ThreadGroup? >? >(threadGroup,"groups")? .filterNotNull()? .forEach { printThreads(it) } } printThreads(ThreadGroup::class.get(null."systemThreadGroup")!! }Copy the code

Log output:

I/(MainActivity.kt:34) invoke: group name: system
I/(MainActivity.kt:36) invoke: thread name: Signal Catcher
I/(MainActivity.kt:36) invoke: thread name: HeapTaskDaemon
I/(MainActivity.kt:36) invoke: thread name: ReferenceQueueDaemon
I/(MainActivity.kt:36) invoke: thread name: FinalizerDaemon
I/(MainActivity.kt:36) invoke: thread name: FinalizerWatchdogDaemon
I/(MainActivity.kt:36) invoke: thread name: Profile Saver

I/(MainActivity.kt:34) invoke: group name: main
I/(MainActivity.kt:36) invoke: thread name: main
I/(MainActivity.kt:36) invoke: thread name: Jit thread pool worker thread 0
I/(MainActivity.kt:36) invoke: thread name: Binder:26573_1
I/(MainActivity.kt:36) invoke: thread name: Binder:26573_2
I/(MainActivity.kt:36) invoke: thread name: Binder:26573_3
I/(MainActivity.kt:36) invoke: thread name: Binder:26573_4
I/(MainActivity.kt:36) invoke: thread name: RenderThread
I/(MainActivity.kt:36) invoke: thread name: magnifier pixel copy result handler
I/(MainActivity.kt:36) invoke: thread name: queued-work-looper
I/(MainActivity.kt:36) invoke: thread name: DefaultDispatcher-worker-1
I/(MainActivity.kt:36) invoke: thread name: DefaultDispatcher-worker-2
I/(MainActivity.kt:36) invoke: thread name: DefaultDispatcher-worker-3
Copy the code

As you can see, there are now two thread groups in the process: System and main.

Signal Catcher looks familiar, but I can’t find it in the source code. Okay, knowledge blind, I surrender.

Next, there are four Daemon threads and search globally for any one of them:

They’re all in a class called Daemons. Find an article: www.freesion.com/article/240…

It explains what these four threads do:

  1. HeapTaskDaemon:Used to free heap memory;
  2. ReferenceQueueDaemon:Some soft, weak, and virtual reference objects are added to the corresponding ReferenceQueue when they are reclaimed. This operation is handled by the ReferenceQueueDaemon.
  3. FinalizerDaemon:To call back and forth [object to be reclaimed]finalizeMethods;
  4. FinalizerWatchdogDaemon:Monitor the FinalizerDaemon thread if it is calling back the object to be reclaimedfinalizeMethod exceeds 100_0000_0000 nanoseconds (10 seconds), then the process is forcibly killed;

That last one, Profile Saver, doesn’t know what it does.

The main thread group has a large number of threads:

  1. main:I don’t have to tell you that’s the main thread;
  2. Jit thread pool worker thread 0:
  3. Binder:26573_1, Binder:26573_2, Binder:26573_3, 26573_4: *Bind communication thread;
  4. RenderThread: the thread used to synchronize BlockingGLTextureView;
  5. Magnifier Pixel copy result handler: * Does not know why this is available;
  6. * Queued-work-looper: * This is a HandlerThread (with looper);
  7. *DefaultDispatcher-worker-123: * Since my test Demo uses coroutines, these are thread pools in the Coroutines library;

reference

The source code

Android Messaging -Handler · Leo’s Studio

Why doesn’t the main thread in Android get stuck because of the dead loop in looper.loop ()? – zhihu

multithreading – Android default threads and their use – Stack Overflow

Daily asking | launched the Activity of the app at least a few threads? – Play Android – wanandroid.com