Inventory of Handler's classic dozens of consecutive questions

Author: Ah He

1. Talk about the understanding of Handler

//\frameworks\base\core\java\android\os\Handler.java
//\frameworks\base\core\java\android\os\Looper.java
//\frameworks\base\core\java\android\os\Message.java
//\frameworks\base\core\java\android\os\MessageQueue.java
  1. Android Handler is a set of asynchronous messaging mechanism (asynchronous communication mechanism). It is mainly applicable to the information transfer between different threads in the same component (or in the same file).

  2. Sometimes it is necessary to perform time-consuming IO operations in sub-threads, which may be reading files or accessing the network, etc. After the time-consuming operations are completed, some changes may be required on the UI. Due to the limitations of the Android development specification, we cannot Access the UI control in the child thread, otherwise it will trigger a program exception. At this time, the operation of updating the UI can be switched to the main thread through the Handler.

  3. The Handler mechanism consists of four components: Handler, Message, MessageQueue and Looper

Message is the information passed between threads, it can carry a small amount of information internally, and is used to exchange data between different threads;

Handler, as the name suggests, means processor, and it is mainly used to send and process messages. Sending messages is generally used Handler.sendMessage(), and after a series of processing, the sent messages will eventually be delivered to Handler.handleMessage();

MessageQueue means message queue, which is mainly used to store all messages sent through Handler. This part of the message will always exist in the message queue, waiting to be processed, which is essentially a one-way linked list sorted by time;

Looper is the steward of MessageQueue in each thread. After calling the loop() method of Looper, it will enter an infinite loop, and then whenever a message is found in MessageQueue, it will be taken out and passed to the Handler.handleMessage()method .

  1. How to use Handler
  * <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(Looper.myLooper()) {
  *              public void handleMessage(Message msg) {
  *                  // process incoming messages here
  *              }
  *          };
  *
  *          Looper.loop();
  *      }
  *  }</pre>
  */

My own understanding of the use of the Handler mechanism:

a. Looper.prepare()The essence is to create a Looper, and the core essence of creating a Looper is to create a MessageQueue. Looper can be regarded as the engine of the pipeline, and MessageQueue is the tray on the belt of the pipeline;

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");
    }
    sThreadLocal.set( new Looper(quitAllowed) );
}
​
private Looper(boolean quitAllowed) {
    mQueue =  new MessageQueue(quitAllowed) ;
    mThread = Thread.currentThread();
}

b. new Handler(Looper.myLooper())It is to tell the Handler (worker) which assembly line to handle, Handler.sendMessage()that is, to let the Handler (worker) put the parts (message) to be processed on the tray on the assembly line belt, and Handler.dispatchMessage()to put the parts on the tray at the end of the assembly line ( message) is taken out for processing;
c. Looper.loop()It is to start the pipeline engine Looper to make the pipeline run;


a. The parts (Message) on the conveyor belt are first in first out. Every time a worker puts a part (Message) on the tray on the conveyor belt (MessageQueue), he will consider the priority of which part needs to be processed, and put it into the conveyor belt MessageQueue according to the priority order that needs to be processed;

b. Every time the worker (Handler) finishes processing the parts (message), he will collect the pallets and throw them into the pallet pile (sPool). This pallet pile can hold up to 50, and then when the workers need to put parts and use the pallet, Just take it directly from here, saving time;

c. When the parts (message) in the tray on the assembly line belt have not reached the time to be processed, the Looper will stop the transmission and enter the standby state;

d. A complete pipeline has only one engine (Looper) and one conveyor belt (MessageQueue), while there can be multiple workers (Handler). When a worker places a part (Message) on the conveyor belt, he will add the worker's (Handler) personal information (msg. worker handle(dispatchMessage(msg));

e. When there is no part (Message) on the conveyor belt (MessageQueue), the engine (Looper) no longer runs and enters the standby state, and the entire system is blocked in the state of preparing to take the next part (MessageQueue.next()), in fact Call the underlying method of nativePollOnce, once there are new parts (Message) on the conveyor belt, the pipeline system will continue to run;

2. How many Loopers does a thread have? How many messageQueues are there? How to guarantee?

A thread has only one Looper object and one MessageQueue object.

Before creating the Handler, it needs to be called Looper.prepare(). This function ensures that each thread has only one Looper object.

/** 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");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}

The MessageQueue is created in the Looper constructor, which ensures that a Looper corresponds to a MessageQueue

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

3. How many handlers can a thread have? So how to ensure that the message can be assigned to the correct handler for processing?

Because Handler can be new in Activity, it can also be new in Service, and all Activities run in the main thread, which proves that there can be multiple Handlers in the main thread.

When the Handler is in sendMessageAtTime(), it will fill in itselfmsg.target

public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
    boolean sent = false;
    MessageQueue queue = mQueue;
    if (queue != null) {
         msg.target = this; 
        sent = queue.enqueueMessage(msg, uptimeMillis);
    }
    else {
        RuntimeException e = new RuntimeException(
            this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
    }
    return sent;
}

Then Looper.loop()when continuously getting Message processing from MessageQueue, it will call the corresponding dispatchMessage according to msg.target, where msg.target is the previous handler

public static final void loop() {
    Looper me = myLooper();
    MessageQueue queue = me.mQueue;
    while (true) {
        Message msg = queue.next(); // might block
        if (msg != null) {
            if (msg.target == null) {
                return;
            }
            if (me.mLogging!= null) me.mLogging.println(
                    ">>>>> Dispatching to " + msg.target + " "
                    + msg.callback + ": " + msg.what
                    );
             msg.target.dispatchMessage(msg); 
            if (me.mLogging!= null) me.mLogging.println(
                    "<<<<< Finished to    " + msg.target + " "
                    + msg.callback);
            msg.recycle();
        }
    }
}

4. What is the data structure of MessageQueue? Multiple threads add data to MessageQueue, how to ensure thread safety?

MessageQueue is a first-in first-out data structure. The underlying implementation is a one-way linked list sorted by time. When a Message enters the queue, it is sorted and inserted according to the when value of the Message.

Synchronized locks are used in MessageQueue.enqueueMessage()and to ensure thread safetyMessageQueue.next()

final boolean enqueueMessage(Message msg, long when) {
    if (msg.when != 0) {
        throw new AndroidRuntimeException(msg + " This message is already in use.");
    }
    if (msg.target == null && !mQuitAllowed) {
        throw new RuntimeException("Main thread not allowed to quit");
    }
      synchronized (this) { 
        if (mQuiting) {
            RuntimeException e = new RuntimeException(msg.target + " sending message to a Handler on a dead thread");
            Log.w("MessageQueue", e.getMessage(), e);
            return false;
        } else if (msg.target == null) {
            mQuiting = true;
        }
        msg.when = when;
        Message p = mMessages;
         if (p == null || when == 0 || when < p.when) {
            msg.next = p;
            mMessages = msg;
            this.notify();
        } else {
            Message prev = null;
            while (p != null && p.when <= when) {
                prev = p;
                p = p.next;
            }
            msg.next = prev.next;
            prev.next = msg; 
            this.notify();
        }
    }
    return true;
}
​
Message next() {
    final long ptr = mPtr;
    if (ptr == 0) {
        return null;
    }
​
    int pendingIdleHandlerCount = -1; // -1 only during first iteration
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
        nativePollOnce(ptr, nextPollTimeoutMillis);
        synchronized (this) {
            // Try to retrieve the next message.  Return if found.
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            if (msg != null && msg.target == null) {
                // Stalled by a barrier.  Find the next asynchronous message in the queue.
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                if (now < msg.when) {
                    // Next message is not ready.  Set a timeout to wake up when it is ready.
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // Got a message.
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                    msg.markInUse();
                    return msg;
                }
            } else {
                // No more messages.
                nextPollTimeoutMillis = -1;
            }
        ...
    }
}

5. Why the main thread can new Handler? What work does the child thread need to do if it wants a new handler?

Looper.prepareMainLooper()Because and have been added in advance in the main thread Looper.loop(), if the child thread wants to call the new handler, it needs to call Looper.prepare()andLooper.loop()

//\frameworks\base\core\java\android\app\ActivityThread.java

public static void main(String[] args) {
    ...
     Looper.prepareMainLooper(); 

    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();
    thread.attach(false, startSeq);

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

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

     Looper.loop(); 
}

csharp

public static void prepareMainLooper() {
     prepare(false) ;
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

6. Is the message time of Handler.postDelayed() accurate? Implementation principle?

In fact, this problem is the same as thread safety. Once the thread is safe in multithreading, the time cannot be accurate; once the time is accurate, the thread must not be safe. Because when multiple threads access the queue, they will be locked when putting in the queue and taking out messages. When the first thread has not completed the access, the second thread cannot be used, so its actual time will be different. It is delayed. Therefore, the time of the Delayed message sent by the Handler is basically accurate, but not completely accurate.

Implementation principle : postDelayedFinally, one with a delay parameter will be called sendMessageAtTime, and then MessageQueue.enqueueMessagethe msg with a delay time parameter will be inserted into the MessageQueue in time order. MessageQueue is a one-way linked list sorted by time. When Looper fetches msg from MessageQueue, it will judge whether the current time has reached the delay time of the first msg at the head of the linked list. If it has not yet arrived, it will calculate by comparing the delay time with the current time Find out the waiting time, and then nativePollOnceperform a blocking wait through the native function until the waiting time is up and then wake up the thread to execute msg;

7. What happens when there is no message in the MessageQueue? Why doesn't Looper.loop block the main thread?

When the MessageQueue queue is empty, Looper.loop()the infinite loop will not exit or execute, but will be blocked in the method MessageQueue.next()in nativePollOnce()and enter a dormant state, waiting for new messages to arrive and wake up again. This will involve the implementation of the pipe and epoll mechanism of the underlying linux.

8. Why does the Handler infinite loop not get stuck?

The ANR stuck in the application has nothing to do with the Looper's infinite loop . When the application has no message to process, it is sleeping and the thread is released; and ANR means that the message has not been processed in time, such as the button and touch event are not processed within 5s, or the foreground broadcast is not processed within 10s, etc., resulting in Stuck;

9. Does IdleHandler understand?

IdelHandler is a static internal interface in MessageQueue. When the msg obtained by Looper from MessageQueue is empty, or when the execution time is not up, that is, when MessageQueue is idle, it will call back IdleHandler.queueIdle().

If queueIdle()it returns false, the idlehandler will be removed after execution, that is, it will be executed only once, if it returns true, it will be kept, and the next time MessageQueue enters the idle state to continue execution;

//\frameworks\base\core\java\android\os\MessageQueue.java

/**
 * Callback interface for discovering when a thread is going to block
 * waiting for more messages.
 */
public static interface IdleHandler {
  /**
   * Called when the message queue has run out of messages and will now
   * wait for more.  Return true to keep your idle handler active, false
   * to have it removed.  This may be called if there are still messages
   * pending in the queue, but they are all scheduled to be dispatched
   * after the current time.
   */
  boolean queueIdle();
}

Message next() {
    ......
    for (;;) {
        ......
        synchronized (this) {
        // 此处为正常消息队列的处理
            ......
            if (mQuitting) {
                dispose();
                return null;
            }
            if (pendingIdleHandlerCount < 0
                    && (mMessages == null || now < mMessages.when)) {
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
            if (pendingIdleHandlerCount <= 0) {
                // No idle handlers to run.  Loop and wait some more.
                mBlocked = true;
                continue;
            }
            if (mPendingIdleHandlers == null) {
                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
            }
            //mIdleHandlers 数组,赋值给 mPendingIdleHandlers
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }
        for (int i = 0; i < pendingIdleHandlerCount; i++) {
            final IdleHandler idler = mPendingIdleHandlers[i];
            mPendingIdleHandlers[i] = null; // release the reference to the handler
            boolean keep = false;
            try {
                 keep = idler.queueIdle(); 
            } catch (Throwable t) {
                Log.wtf(TAG, "IdleHandler threw exception", t);
            }
            if (!keep) {
                synchronized (this) {
                    mIdleHandlers.remove(idler);
                }
            }
        }
        pendingIdleHandlerCount = 0;
        nextPollTimeoutMillis = 0;
    }
}

Example of use in the system source code : ActivityThread.handleResumeActivity()In the middle, after the onResume method is executed, it is called Looper.myQueue().addIdleHandler(new Idler())to perform some less urgent tasks such as resource recovery and log printing. In addition, IdleHandler can also be used when doing project performance optimization , which executes tasks when the main thread is idle without affecting the execution of other tasks.

@Override
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
        String reason) {
    // If we are getting ready to gc after going to the background, well
    // we are back active so skip it.
    unscheduleGcIdler();
    mSomeActivitiesChanged = true;
        ···
        //该方法最终会执行 onResume方法
    final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
    if (r == null) {
        // We didn't actually resume the activity, so skipping any follow-up actions.
        return;
    }
        ··· 
        ···
    r.nextIdle = mNewActivities;
    mNewActivities = r;
    if (localLOGV) Slog.v(TAG, "Scheduling idle handler for " + r);
     Looper.myQueue().addIdleHandler(new Idler()); 
}

private class Idler implements MessageQueue.IdleHandler {
    @Override
    public final boolean queueIdle() {
        ActivityClientRecord a = mNewActivities;
            ···
        if (a != null) {
            mNewActivities = null;
            IActivityManager am = ActivityManager.getService();
            ActivityClientRecord prev;
            do {
                //打印一些日志
                if (localLOGV) Slog.v(
                    TAG, "Reporting idle of " + a +
                    " finished=" +
                    (a.activity != null && a.activity.mFinished));
                if (a.activity != null && !a.activity.mFinished) {
                    try {
                        //AMS 进行一些资源的回收
                        am.activityIdle(a.token, a.createdConfig, stopProfiling);
                        a.createdConfig = null;
                    } catch (RemoteException ex) {
                        throw ex.rethrowFromSystemServer();
                    }
                }
                prev = a;
                a = a.nextIdle;
                prev.nextIdle = null;
            } while (a != null);
        }
        if (stopProfiling) {
            mProfiler.stopProfiling();
        }
        
        //确认Jit 可以使用,否则抛出异常
        ensureJitEnabled();
        return false;
    }
}

10. Do you understand the synchronization barrier?

It can be understood that the synchronization barrier is a priority strategy provided for the Handler message mechanism , which can improve the priority of asynchronous messages .

There are three types of messages in the Handler mechanism: synchronous messages, asynchronous messages, and barrier messages. The messages we use normally are all synchronous messages. Asynchronous messages can be set when the Handler is constructed, or can be set through the settings. The difference between barrier messages and synchronous messages setAsynchronousis The target property is null . When Looper fetches messages from MessageQueue, if no barrier message is encountered, the synchronous message and asynchronous message are the same. If a barrier message is encountered, all synchronous messages after the message will be blocked, and only asynchronous messages will be executed.

In the UI drawing process , synchronous barriers and asynchronous messages are used to ensure that when the Vsync signal comes, the asynchronous tasks can be processed first, so that the drawing tasks can be executed in time to avoid interface freeze.

@UnsupportedAppUsage
Message next() {
    for (;;) {
        nativePollOnce(ptr, nextPollTimeoutMillis);
        synchronized (this) {
            Message msg = mMessages;
             //如果msg.target为空,也就是说是一个同步屏障消息,则进入这个判断里面
            if (msg != null && msg.target == null) {
                // Stalled by a barrier.  Find the next asynchronous message in the queue.
                //在这个while循环中,找到最近的一个异步消息
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous()); 
            }
            //找到了异步消息
            if (msg != null) {
                //如果消息的处理时间小于当前时间 则等待
                if (now < msg.when) {
                    // Next message is not ready.  Set a timeout to wake up when it is ready.
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // Got a message.
                    //处理消息
                    mBlocked = false;
                    //将异步消息移除
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                    msg.markInUse();
                    //返回异步消息
                    return msg;
                }
            } else {
                // No more messages.
                //没有找到异步消息则进入阻塞状态,等待被唤醒
                nextPollTimeoutMillis = -1;
            }
            ...
}

In addition, it should be noted that the synchronization barrier will not be removed automatically, and it needs to be removed manually after use, otherwise the synchronization message will not be processed. That is, as mentioned above, through removeSyncBarrier(int token)the method to remove, the token is the token returned when the barrier was added before.

public void removeSyncBarrier(int token){
}

11. Why does the Handler leak memory? How to solve?

reason

The Handler's Message is stored in the MessageQueue. Some Messages cannot be processed immediately, and they will exist in the MessageQueue for a long time, which will cause the Handler to fail to be recycled. Because Handler is an instance of a non-static anonymous inner class, it will implicitly hold the outer class Activity, so that the Activity cannot be recycled, causing the Activity to leak memory

Solution

  1. Use a static modified handler and use a weak reference to the activity object, because you need to use the members in the activity object (because the static inner class does not hold references to the outer class, so using a static handler will not cause the activity to leak);
  2. Define the handler separately, and also weakly reference the activity;
  3. Use the handler of the inner class to removeCallbacksAndMessages in the onDestroy method;
  4. Another method is to directly use the Handler open source library WeakHandler to avoid memory leaks;

12. How to create a Message? Why?

To create a Message, it is recommended to use Message.obtain() instead of new message(). Because the Message class maintains an sPool object, it can be understood as a Message linked list, and the default maximum length of this linked list is 50. In the Android message mechanism, whenever a Message object is processed, it will be put into this pool, providing us with reuse. When we call Message.obtain()a method, we don't create a new Message object if we reuse the Message object that exists in the pool. This avoids the performance overhead caused by frequent creation and destruction of Message objects . Reduce memory jitter and OOM.

  /** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}).
  */
  public Message() {
  }
  
   public static Message obtain() { 
    synchronized (sPoolSync) {
        if (sPool != null) {
            Message m = sPool;
            sPool = m.next;
            m.next = null;
            m.flags = 0; // clear in-use flag
            sPoolSize--;
            return m;
        }
    }
    return new Message();
}

13. HandlerThread

Handlerthread is a class inherited from Thread. It is used to open a new thread with a looper. You can use this looper to create a Handler. It should be noted that the method must be called to open the thread start(). Because start()you can call run()the method of the thread, and HandlerThread.run()in will call Looper.prepare()and Looper.loop()to bind the Looper for the child thread, which is equivalent to a layer of encapsulation, which is convenient for users to use Handler.

//frameworks\base\core\java\android\os\HandlerThread.java

/*
Handy class for starting a new thread that has a looper.
The looper can then be used to create handler classes.
Note that start() must still be called.
*/

public class HandlerThread extends Thread {
  ...

  @Override
  public void run() {
      mTid = Process.myTid();
      Looper.prepare();// HandlerThread在start()的时候执行run()方法,而Looper就是在这里被创建的
      synchronized (this) {
          mLooper = Looper.myLooper();
          notifyAll();
      }
      Process.setThreadPriority(mPriority);
      onLooperPrepared();
      Looper.loop();
      mTid = -1;
  }
}

How to use Handler and HandlerThread together

HandlerThread thread1 = new HandlerThread("test1");
thread1.start();

Handler mHandler = new Handler(thread1.getLooper()){
  @Override
  public void handleMessage(Message msg) {
      switch (msg.what) {
          case DownloadThread.TYPE_START:
              Log.e(TAG, "4.主线程知道Download线程开始下载了...这时候可以更改主界面UI");
              break;
          default:
              break;
      }
      super.handleMessage(msg);
  }
}

14. IntentService

IntentService is a basic class inherited from Service, and is essentially a Service. It is mainly used to respond to Intent-based asynchronous requests. The client startService(Intent)sends a request through , and the IntentService starts after receiving the request, and processes the asynchronous Intent requests sequentially in the newly created sub-thread, and only one request will be processed at the same time. When all requests are complete, the IntentService closes itself.

Why is IntentService provided after the official Service is provided?

Service runs on the main thread by default. If we need to process some time-consuming tasks in Service, then we need to manually create threads or use thread pools to process time-consuming tasks (otherwise ANR will occur), and then after processing In the future, manually shut down the Service, and IntentService has already done this work for us. We only need to onHandleIntentwrite the code of the time-consuming task in it, and then it can be executed in the sub-thread, because onHandleIntentit runs in the sub-thread, and in the task After execution, IntentService will execute stopSelf(startId)the method by itself and close itself.

What are the benefits of using IntentService?

First, we save the trouble of manually opening threads in the Service, and second, we don't need to manually stop the Service when the operation is completed.

For an example of using ServiceIntent, please refer to the source code; the execution effect is as follows:

//\frameworks\base\core\java\android\app\IntentService.java

/**
 * IntentService is an extension of the {@link Service} component class that
 * handles asynchronous requests (expressed as {@link Intent}s) on demand.
 * Clients send requests
 * through {@link android.content.Context#startService(Intent)} calls; the
 * service is started as needed, handles each Intent in turn using a worker
 * thread, and stops itself when it runs out of work.
 */
@Deprecated
public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;
    @UnsupportedAppUsage
    private volatile ServiceHandler mServiceHandler;
    private String mName;
    private boolean mRedelivery;

    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
    }

    public IntentService(String name) {
        super();
        mName = name;
    }
    ...

    @Override
    public void onCreate() {
        super.onCreate();
        //这边就使用到了 HandlerThread 的工具类
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        mServiceLooper = thread.getLooper();
        // mServiceHandler 本质是 Handler,使用了 mServiceLooper 去创建
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }

    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        mServiceLooper.quit();
    }

    /**
     * This method is invoked on the worker thread with a request to process.
     * Only one Intent is processed at a time, but the processing happens on a
     * worker thread that runs independently from other application logic.
     * So, if this code takes a long time, it will hold up other requests to
     * the same IntentService, but it will not hold up anything else.
     * When all requests have been handled, the IntentService stops itself,
     * so you should not call {@link #stopSelf}.
     *
     * @param intent The value passed to {@link
     *               android.content.Context#startService(Intent)}.
     *               This may be null if the service is being restarted after
     *               its process has gone away; see
     *               {@link android.app.Service#onStartCommand}
     *               for details.
     */
    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);
}
  • The epoll mechanism of linux

    • The epoll mechanism is an IO multiplexing mechanism that can monitor multiple descriptors at the same time. When a descriptor is ready (read or write ready), it immediately notifies the corresponding program to perform a read or write operation, essentially synchronous I/O , that is, reading and writing are blocked. Therefore, the main thread is dormant most of the time and does not consume a lot of CPU resources.
/**
 * 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.
 *
 * <p>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.
 *
 * <p>Scheduling messages is accomplished with the
 * {@link #post}, {@link #postAtTime(Runnable, long)},
 * {@link #postDelayed}, {@link #sendEmptyMessage},
 * {@link #sendMessage}, {@link #sendMessageAtTime}, and
 * {@link #sendMessageDelayed} methods.  The <em>post</em> versions allow
 * you to enqueue Runnable objects to be called by the message queue when
 * they are received; the <em>sendMessage</em> versions allow you to enqueue
 * a {@link Message} object containing a bundle of data that will be
 * processed by the Handler's {@link #handleMessage} method (requiring that
 * you implement a subclass of Handler).
 * 
 * <p>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.
 * 
 * <p>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 <em>post</em> or <em>sendMessage</em> 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.
 */
public class Handler {

}

If you still haven't fully grasped Handler and want to understand it in the shortest time, you can refer to "Android Framework Core Knowledge Points" , which includes: Init, Zygote, SystemServer, Binder, Handler, AMS, PMS , Launcher... and other knowledge points records.

"Framework Core Knowledge Point Summary Manual" :https://qr18.cn/AQpN4J

Part of the implementation principle of the Handler mechanism:
1. Macro-theoretical analysis and Message source code analysis
2. MessageQueue source code analysis
3. Looper source code analysis
4. Handler source code analysis
5. Summary

Binder principle:
1. Knowledge points that must be understood before learning Binder
2. Binder mechanism in ServiceManager
3. System service registration process
4. ServiceManager startup process
5. System service acquisition process
6. Java Binder initialization
7. Java The registration process of system services in Binder

Zygote :

  1. The startup process of the Android system and the startup process of Zygote
  2. The startup process of the application process

AMS source code analysis:

  1. Activity life cycle management
  2. onActivityResult execution process
  3. Detailed Explanation of Activity Stack Management in AMS

In-depth PMS source code:

1. PMS startup process and execution process
2. APK installation and uninstallation source code analysis
3. Intent-filter matching structure in PMS

WMS:
1. The birth of WMS
2. The important members of WMS and the addition process of Window
3. The deletion process of Window

"Android Framework Study Manual":https://qr18.cn/AQpN4J

  1. Boot Init process
  2. Start the Zygote process at boot
  3. Start the SystemServer process at boot
  4. Binder driver
  5. AMS startup process
  6. The startup process of the PMS
  7. Launcher's startup process
  8. The four major components of Android
  9. Android system service - distribution process of Input event
  10. Android underlying rendering-screen refresh mechanism source code analysis
  11. Android source code analysis in practice

Guess you like

Origin blog.csdn.net/weixin_61845324/article/details/132623649