IntentService source code parsing and HandlerThread usage on Android framework

Posted by shibbi3 on Sat, 04 Apr 2020 07:34:44 +0200

Two things that are easy to use

As an upgraded version of Service, IntentService increases the ability to use threads in the Service. However, if the Service runs in the main thread, time-consuming operations will cause ANR. The benefits of IntentService are self-evident. It can not only handle time-consuming operations, but also has a higher priority than threads. At the same time, the Service will automatically stop after the completion of execution. Next, we will explore how to implement these functions from the source point of view.

Open IntentService first
I'll post them all if the source code is not many:
The first thing you can see is that it inherits from the Service
And then there's a layer of HandlerThread inside
Bind handler to HandlerThread while creating service
The task will be executed in HandlerThread
Note: the task is constantly fetched out by the Looper in the Messagequeue and executed by the Handler
Therefore, only one task can be executed at a time, and IntentService cannot execute tasks concurrently

public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;
    private volatile ServiceHandler mServiceHandler;
    private String mName;
    private boolean mRedelivery;

    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }
// onHandleIntent processes the task information here. After processing, the stopSelf service is stopped automatically.
        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent)msg.obj);
//Note that this does not mean that after a request is completed, the service stops, because the service may receive a new start request. stopSelf has a parameter startId, which is equal to the ID generated when the service is finally started.
            stopSelf(msg.arg1);
        }
    }
    public IntentService(String name) {
        super();
        mName = name;
    }

    public void setIntentRedelivery(boolean enabled) {
        mRedelivery = enabled;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        // You can see that when the service is created, the HandlerThread is created and a Handler is bound to the HandlerThread (through the Looper of the incoming thread)

        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();
        mServiceLooper = thread.getLooper();
        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) {
    //Every time startService is called, this method will be called, and the message will be sent to ServiceHandler for processing, which is a HandlerThread thread
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }

    // When you destroy a service, you exit the loop. In fact, you end the thread
    @Override
    public void onDestroy() {
        mServiceLooper.quit();
    }

    @Override
    @Nullable
     // This is used to communicate with Activity
    public IBinder onBind(Intent intent) {
        return null;
    }
    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);
}

By the way, how to use HandlerThread

  HandlerThread handlerThread = new HandlerThread("name");
  // Start start is to start the Looper inside the HandlerThread. See the source code below for details
  handlerThread.start();
  // Next, just give the loop object to the Handler to complete the binding
  // Otherwise, the default handler is the Looper of the main thread. Of course, if the UI is updated, we can use this handler to send messages in the thread, and then the handler's callback method will run in the main thread, so that the asynchronous request to update the UI can be realized
  Handler handler = new Handler(handlerThread.getLooper());
  // The message sent here will send the task to the processing queue, and the Looper will continuously take out the Runnable and submit it to the Handler for processing
  handler.post(new Runnable() {
            @Override
            public void run() {

            }
        });

HandlerThread inherits Thread and has a Looper inside. Calling start() will start the Looper

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