IntentService is a subclass of Service. No time-consuming operation can be performed in Service. So Google provides an IntentService that maintains a child thread internally to perform the operation and automatically destroys the Service when the task is completed. When multiple tasks come, they are executed in sequence. So how does IntentService work?

The use of the IntentService

class IntentServiceTest extends IntentService { public IntentServiceTest() { super("IntentServiceTest"); } @override protected void onHandleIntent(@nullable Intent Intent) {Copy the code

IntentService is very simple to use. You just implement the onHandleIntent method, which is inside the child thread. IntentService has a setIntentRedelivery(Boolean Enabled) method. If the Service is killed before the task is completed, the Service will restart when the memory is full. And the last incoming task will be re-executed. If you pass flase, the unfinished task will not be re-executed.

Note: When there are multiple unfinished tasks, only the last incoming task will be executed.

Next, the implementation of IntentService is analyzed.

InetentService source code analysis

Let’s start with IntentService’s onCreate method.

private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
@Override
public void onCreate() {
    super.onCreate();
    HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
    thread.start();
    mServiceLooper = thread.getLooper();
    mServiceHandler = new ServiceHandler(mServiceLooper);
}
Copy the code

When IntentService is created, it initializes a HandlerThread and creates a ServiceHandler. The task will be passed to the mServiceHandler for execution.

Next, take a look at how the task is performed.

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); }}@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 onStart(@Nullable Intent intent, int startId) {
    Message msg = mServiceHandler.obtainMessage();
    msg.arg1 = startId;
    msg.obj = intent;
    mServiceHandler.sendMessage(msg);
}
@Override
public void onDestroy(a) {
    mServiceLooper.quit();
}
Copy the code

When startService is called, it goes to onStartCommad and calls the onStart method in the onStartCommad method. Pass the intent and startid. onStart create Message pass the intent and startId as obj and parameters. Onhandlemessage calls the onHandleIntent method first. OnHandleIntent completes the processing of data. After the task is completed, the stopSelf method is called to destroy the service.

The stopSelf method is called to stop the service, so when multiple tasks come, the following tasks will not be executed. That, of course, is impossible. The stopSelf method is called with a startId that is passed a new value to onStartCommand every time startService is called. Call stopSelf. If a value greater than 0 is passed in, it will determine whether the value is equal to the last startId passed in. If it was the last Service, it will go to the onDestory process.

The stopSelf process is as follows:

Service.java#stopSelf->ActivityManagerService.java#stopServiceToken->ActiveServices.java#stopServiceTokenLocked

boolean stopServiceTokenLocked(ComponentName className, IBinder token,
        int startId) {
    if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "stopServiceToken: " + className
            + "" + token + " startId=" + startId);
    ServiceRecord r = findServiceLocked(className, token, UserHandle.getCallingUserId());
    if(r ! =null) {
        if (startId >= 0) {
            // Asked to only stop if done with all work. Note that
            // to avoid leaks, we will take this as dropping all
            // start items up to and including this one.
            ServiceRecord.StartItem si = r.findDeliveredStart(startId, false.false);
            if(si ! =null) {
                while (r.deliveredStarts.size() > 0) {
                    ServiceRecord.StartItem cur = r.deliveredStarts.remove(0);
                    cur.removeUriPermissionsLocked();
                    if (cur == si) {
                        break; }}}/ / code 1
            if(r.getLastStartId() ! = startId) {return false;
            }

            if (r.deliveredStarts.size() > 0) {
                Slog.w(TAG, "stopServiceToken startId " + startId
                        + " is last, but have " + r.deliveredStarts.size()
                        + " remaining args"); }}synchronized (r.stats.getBatteryStats()) {
            r.stats.stopRunningLocked();
        }
        r.startRequested = false;
        if(r.tracker ! =null) {
            r.tracker.setStarted(false, mAm.mProcessStats.getMemFactorLocked(),
                    SystemClock.uptimeMillis());
        }
        r.callStart = false;
        final long origId = Binder.clearCallingIdentity();
        bringDownServiceIfNeededLocked(r, false.false);
        Binder.restoreCallingIdentity(origId);
        return true;
    }
    return false;
}
Copy the code
  • As you can see in code 1, if startId is not the last one, false is returned instead of stopping the service.

Let’s look at why setIntentRedelivery passes true and why the last task (if not completed) will be executed and why other unfinished tasks will not be re-executed.

Analysis of setIntentRedelivery method

public void setIntentRedelivery(boolean enabled) {
    mRedelivery = enabled;
}
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
    onStart(intent, startId);
    return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
Copy the code

When setIntentRedelivery is true, onStartCommand returns START_REDELIVER_INTENT. After the service is killed, the service will be recreated under certain conditions. The value of the previous Intent is passed directly. Therefore, only the last incoming task is re-executed.