Source code analysis of Android system: Exploration of Handler's extended knowledge

After analyzing the Handler source code, there are many unknown secrets. Next, analyze it.

Classes involved:
HandlerThread, IntentService, AsyncTask, Messenger, IdleHandler, Looper.Observer, MessageLogging, etc

The first half: HandlerThread, IntentService, AsyncTask
In the second half of the lecture: Messenger, IdleHandler, Looper.Observer, MessageLogging

1. HandlerThread

Background: in Android system, message mechanism is used to deliver messages between threads. If you want to pass messages in a child thread, you need to create a Handler for the child thread.

    new Thread(new Runnable() {
        @Override
        public void run() {
            Looper.prepare();

            Handler workHandler = new Handler(new Handler.Callback() {
                @Override
                public boolean handleMessage(@NonNull Message msg) {
                    // Processing messages for child threads
                    return false;
                }
            });

            // send message
            workHandler.sendEmptyMessage(0);

            Looper.loop();
        }
    }).start();

Although this method can realize message delivery in a sub thread, it is very troublesome and the sub thread cannot exit the loop. Android provides us with a good component HandlerThread.

Looking at the source code, it is found that HandlerThread inherits the Thread and maintains a Looper internally. When the run method runs after the Thread is opened, a Looper will be created. It is defined as follows:

/**
 * A {@link Thread} that has a {@link Looper}.
 * The {@link Looper} can then be used to create {@link Handler}s.
 * <p>
 * Note that just like with a regular {@link Thread}, {@link #start()} must still be called.
 */
public class HandlerThread extends Thread {}

Its main function is to deal with single thread time-consuming tasks.

1.1 basic use

After the HandlerThread is created (the name of the thread needs to be specified), the HandlerThread thread must be started first.

HandlerThread handlerThread = new HandlerThread("AsyncThreadTask");
handlerThread.start(); // Startup thread

// When creating a Handler, you need to specify the Looper object created in the HanderThread
Handler workHandler = new Handler(handlerThread.getLooper());

// send message
workHandler.post(new Runnable() {
    @Override
    public void run() {
      // Processing time-consuming tasks
    }
});

// Exit the message loop and the sent message will not be processed
handlerThread.quit();

1.2 source code analysis

The source code of HandlerThread is very simple, only more than 100 lines. First, let's see its construction method

// HandlerThreadde.java source code

   int mPriority; // thread priority 
   public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }
    
    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }

There are two construction method parameters:

  • Name the name of the thread
  • Priority the priority of the thread

After the thread start(), the run method executes. The current thread binds the Looper object and loops the messages in the thread.

// HandlerThreadde.java source code

    Looper mLooper; // Looper object held by current thread

    // Looper has created the call
    protected void onLooperPrepared() {
    }

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare(); // Create Looper object
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll(); // Wake up waiting thread
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop(); // 
        mTid = -1;
    }

Provides a method to get the Looper of the current thread.

// HandlerThreadde.java source code

    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

If the current thread is not running, return null directly; if the current thread is running, but the Looper has not been created, wait, wait for the Looper to be created, otherwise return the Looper object of the thread directly.

2. IntentService

Background: in the Android application program, if the time-consuming operation in Service is easy to appear the phenomenon of ANR, the usual practice is to open a child thread in the onStartCommon method and then execute the time-consuming operation internally. If the service is stopped after execution, it is necessary to manually call stopSelf() in the run method of the child thread to stop the service.

In fact, IntentService is a combination of Service and HandlerThread.

Three basic points of the definition of IntentService: what is it? How to use it? How to work?

/**
  1,What is it?
    IntentService is a base class for {@link Service}s that 
      handle asynchronous requests (expressed as {@link Intent}s) on demand. 

 2,How to use it?
  Clients send requests through 
   {@link android.content.Context#startService(Intent)} calls; 

 3,How to work?
   the service is started as needed, handles each Intent 
   in turn using a worker thread, and stops itself when it runs out of work.
 */
public abstract class IntentService extends Service {}

1. Features of IntentService

  • IntentService is a subclass of the Service class, which is used to process asynchronous requests
  • IntentService is implemented with the help of message queuing, so the execution order of tasks is in the form of a single queue list
  • Since it is a single thread (a worker thread), all tasks need to be queued for execution

2. Advantages and disadvantages of IntentService

The benefits of IntentService compared with Service:

  • Easy to use, simple code, no need to create threads manually in our own Service;
  • When the operation is completed, we do not need to stop the Service manually

But its disadvantages are also obvious:

  • Because it is a single worker thread, tasks need to be queued, which is not suitable for most multitasking situations
  • Not suitable for startup mode like bindService()

2.1 simple use

1. IntentService is an abstract class with an internal abstract method onHandleIntent.

public class MyIntentService extends IntentService {

    public MyIntentService() {
        super("MyIntentService");
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        Log.d(TAG, "onHandleIntent: " + Thread.currentThread().getName());
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy");
    }
}

2. Configuration in Android manifest

// AndroidManifest.xml

<service android:name=".MyIntentService" />

3. Call startService(Intent) to start the IntentService

  Intent service = new Intent(this, MyIntentService.class);
  startService(service);

2.2 simple analysis of source code

1. IntentService is a subclass of Service. It has all the life cycle methods of Service and a ServiceHandler inside

// IntentService.java source code

public abstract class IntentService extends Service {
    // ServiceHandler inherits Handler, and a Looper must be passed in when it is created
    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;
    }

   protected abstract void onHandleIntent(@Nullable Intent intent);
}

2. Source code of onCreate(), create HandlerThread.

// IntentService.java source code

    @Override
    public void onCreate() {
        super.onCreate();

        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

When the IntentService service is turned on, the Handler thread will be created, the Looper object of the current thread will be obtained, and then associated to the Handler.

3. Next is the onStart() source code, which calls mServiceHandler

   @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;
    }

When you start the IntentService, a Message with startId and Intent will be generated and sent to the MessageQueue. Next, when the Looper finds that there is a Message in the MessageQueue, the Handler will stop processing the Message.

4. handleMessage processing code

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

Then call onHandleIntent((Intent)msg.obj), which is an abstract method. In fact, it is the method we want to override. We can handle our work in this method. When the task is completed, we will call stopSelf(msg.arg1) to finish the specified work.

5,stopSelf(msg.arg1)
After the callback is completed, the callback uses stopSelf(msg.arg1). Note that this msg.arg1 is an int value, which is equivalent to the unique ID of a request. Every time a request is sent, a unique ID will be generated, and then the request will be put into the queue. When all the requests are completed (the last request is equivalent to getLastStartId == startId), or the currently sent ID is the latest one (getLastStartId == startId), our Service will be destroyed. If the passed in ID is - 1, it will be destroyed directly.

6,onDestroy()

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

After the service is finished, call this method mServiceLooper.quit() to stop looper.

2.3 problem record

1. In the source handleMessage method, why does the handleIntent method call the stopSelf() with parameters after execution
Because the execution of stopSelf() will immediately stop the service (the internal implementation calls stopSelf with parameters, the value passed in is - 1), and stopSelf with parameters (int startId) will stop the service after all tasks are executed. In general, calling the stopSelf (int satrid) method will not immediately stop the service. It will determine whether the getLastStartId of the recently executed task is equal to the startId. If it is equal, it will immediately stop the service.

3. AsyncTask

In order to simplify the process of accessing UI in sub thread, AsyncTask is provided, but different API versions of AsyncTask have different performance.

Asynctask is a lightweight asynchronous task class, which can perform background tasks in the process pool, and then pass the execution progress and final results to the main thread to update the UI. AsyncTask=Thread+Handler

AsyncTask is an abstract generic class, which provides Params, Progress and Result.

  • Params parameter type
  • Progress the execution progress type of background task
  • Result return result type of background task
public abstract class AsyncTask<Params, Progress, Result> { }

AsyncTask provides four core methods
1. onPreExecute() is executed in the main thread. Before the asynchronous task is executed, this method will be called. It is generally used to do some preparatory work

2. doInBackground(Params... params) is executed in the online process pool. This method is used to execute asynchronous tasks. Params parameters represent the input parameters of asynchronous tasks. In this method, you can update the progress of the task through the publishProgress method, which calls the onProgressUpdate method. This method needs to return the calculation result to the onPostExecute method.

3. onProgressUpdate(Progress... values), which is executed in the main thread, will be called after the publishProgress method is updated when the execution progress of the background task changes.

4. onPostExecute(Result result) is executed in the main thread. After the asynchronous task is executed, this method will be called. The result parameter is the return value of the background task, that is, the return value of doInBackground

In addition to these four methods, the onCancelled() method is also provided. It is also executed in the main thread. When the task is cancelled, it will be called, but onPostExecute will not be called.

Source code analysis of AsyncTask

1. To analyze the working principle of AsyncTask, start with the execute method, which will call the executeOnExecutor method

// AsyncTask.java source code
    @MainThread
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }

    @MainThread
    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
        if (mStatus != Status.PENDING) {
            switch (mStatus) {
                case RUNNING:
                    throw new IllegalStateException("Cannot execute task:"
                            + " the task is already running.");
                case FINISHED:
                    throw new IllegalStateException("Cannot execute task:"
                            + " the task has already been executed "
                            + "(a task can be executed only once)");
            }
        }

        mStatus = Status.RUNNING;

        onPreExecute();

        mWorker.mParams = params;
        exec.execute(mFuture);

        return this;
    }

Sdefaulteexecutor is a serial thread pool in which all asynctasks in a process are queued for execution. In the executeOnExecutor method, the onPreExecute method of AsyncTask executes first, and then the thread pool begins to execute.

2. Analyze the execution process of thread pool

    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

    private static class SerialExecutor implements Executor {
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;

        public synchronized void execute(final Runnable r) {
           // Insert a task to the end of the queue
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        r.run();
                    } finally {
                        scheduleNext();
                    }
                }
            });
            if (mActive == null) {
                scheduleNext();
            }
        }

        protected synchronized void scheduleNext() {
            // Get a task from the head of the queue and assign it to maactive. When the queue is empty, maactive is null
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

The execute method of the SerialExecutor inserts a task into the task queue mTasks. If there is no active task at this time, the scheduleNext method of the SerialExecutor will be called to execute the next task. After the task is executed, the next task will be executed from the queue. If there is no task in the task queue, the next task will be executed.

3. There are two thread pools (SerialExecutor and thread pool executor) and one Handler (InternalHandler) in AsyncTask.

  • Thread pool SerialExecutor for task queuing
  • Thread pool thread pool executor is used to actually execute tasks
  • InternalHandler is used to switch from thread pool to main thread execution

In the construction method of AsyncTask, since the run of FutureTask will call the call method of mWorker, the call method of mWorker will finally execute in the process pool.

// The construction method of AsyncTask

        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);
                Result result = null;
                try {
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    //noinspection unchecked
                    result = doInBackground(mParams);
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    postResult(result);
                }
                return result;
            }
        };

        mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
                try {
                    postResultIfNotInvoked(get());
                } catch (InterruptedException e) {
                    android.util.Log.w(LOG_TAG, e);
                } catch (ExecutionException e) {
                    throw new RuntimeException("An error occurred while executing doInBackground()", e.getCause());
                } catch (CancellationException e) {
                    postResultIfNotInvoked(null);
                }
            }
        };

After doInBackground executes, pass the returned result to the postResult method

    private Result postResult(Result result) {
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }

    private static class InternalHandler extends Handler {
        public InternalHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
            switch (msg.what) {
                case MESSAGE_POST_RESULT:
                    // There is only one result
                    result.mTask.finish(result.mData[0]);
                    break;
                case MESSAGE_POST_PROGRESS:
                    result.mTask.onProgressUpdate(result.mData);
                    break;
            }
        }
    }

Handler will call the finish method of AsyncTask after receiving the message "message" post "result

    private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }

If the message is cancelled, execute the onCancelled method, otherwise call the onPostExecute method.

Keywords: Android Java xml

Added by sgalonska on Wed, 27 Nov 2019 13:09:33 +0200