Android - Service's onDestroy invoked before onStartCommand

     Android's service life cycle seems straight forward and for cases when a Service is started explicitly, developers expect callbacks onCreate() followed by onStartCommand() and eventually a onDestroy() callback as the service is stopped. However, android's service life cycle documentation has the following,

Services can use their stopSelf(int) method to ensure the service is not stopped until started intents have been processed

   This seems to suggest that its possible for the service to be destroyed even before the intents are processed, i.e, for onStartCommand() to be invoked. So how often can this happen and what influences this behavior? The worst case scenario is obviously for a client to start and stop service immediately in the same thread.

    private void startStopService() {
        Intent intent = new Intent(this, MyService.class);
        startService( intent );
        stopService( intent );
    }

But no luck (tested in Lollipop), Service's onStartCommand() is indeed invoked followed by its onDestroy(). So why would Android even warn developers about this less often possibility? A look at the framework code exposes the possibility,

    The request to start a service basically translates into both onCreate() and onStartCommand() and the request is served by a binder thread in the framework which basically ends up in ActiveServices.realStartServiceLocked

private final void realStartServiceLocked(ServiceRecord r,
            ProcessRecord app, boolean execInFg) throws RemoteException {
                ...
                ...
                ...
                 app.thread.scheduleCreateService(r, r.serviceInfo,
                    mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                    app.repProcState);
                ...
                ...
                ...
                sendServiceArgsLocked(r, execInFg, true);
}

   This binder thread schedules callbacks for onCreate() and onStartCommand() in the process hosting the requested service. Both these calls are one way binder transactions,

public final void scheduleCreateService(IBinder token,
                ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
            updateProcessState(processState, false);
            CreateServiceData s = new CreateServiceData();
            s.token = token;
            s.info = info;
            s.compatInfo = compatInfo;

            sendMessage(H.CREATE_SERVICE, s);
 }

public final void scheduleServiceArgs(IBinder token, boolean taskRemoved, int startId,
            int flags ,Intent args) {
            ServiceArgsData s = new ServiceArgsData();
            s.token = token;
            s.taskRemoved = taskRemoved;
            s.startId = startId;
            s.flags = flags;
            s.args = args;

            sendMessage(H.SERVICE_ARGS, s);
}

   Its possible for the binder thread serving create request to complete but the thread serving start request could get context switched out. Meanwhile, the client application process could have requested to stop the service.

private final void bringDownServiceLocked(ServiceRecord r) {
            ...
            ...
            ...
            r.app.thread.scheduleStopService(r);
            ...
}

 This binder thread in turn schedules callbacks for onDestroy() via one way binder transactions,

public final void scheduleStopService(IBinder token) {
            sendMessage(H.STOP_SERVICE, token);
}

    This binder thread could get more priority than the thread handling the start request and this is how a service could get a onDestroy() callback before onStartCommand().

No comments: