Designing a Service for a two-step task

Viewed 26

I have a DialogFragment that can be launched from anywhere, let’s call it UploadDialogFragment. This fragment allows the user to accomplish two related tasks:

  1. Upload an image (can take up to 1min)
  2. Upload a JSON object with some text and a reference to the saved image

This two tasks need to be accomplished in sequence - you can’t do 2. without having completed 1.. So what really happens is:

  • I start uploading the image (1.)
  • Meanwhile, the user writes the text and adds other info
  • When all is ready, dismiss the dialog and start the second task (2.).

I used to do this with background tasks, but now I’d like to switch to a Service: the whole operation should be completed even if, after dismissing, I force quit the app.

Current design

In my experience I have always used IntentService, so I am a complete newbie. The current flawed design I am moving forward is something like:

public class UploadService extends Service {

    private final Binder binder = new Binder();

    public class Binder extends android.os.Binder {
        UploadService getService() {
            return UploadService.this;
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return START_NOT_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }

    public void completeFirstTask() {
        ...
    }

    public void completeSecondTask() {
        // wait for first task to complete if necessary...
        ...
        stopSelf();
    }

}

And here’s my UploadDialogFragment:

public class UploadDialogFragment extends AppCompatDialogFragment implements
        ServiceConnection {

    private UploadService uploadService;
    private boolean boundService;

    @Override
    public void onServiceDisconnected(ComponentName name) {
        uploadService = null;
        boundService = false;
    }

    private void bindService() {
        Intent i = new Intent(getActivity().getApplicationContext(), UploadService.class);
        getActivity().getApplicationContext().bindService(i, this, Context.BIND_AUTO_CREATE);
        boundService = true;
    }

    private void unbindService() {
        if (boundService) {
            getActivity().unbindService(this);
            boundService = false;
        }
    }

    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        uploadService = ((UploadService.Binder) service).getService();
        uploadService.completeFirstTask();
    }

    // THEN, LATER:
    // OnClick of a button, I call uploadService.completeSecondTask();
    // and this.dismiss();

}

This is deeply flawed right now.

  • I need to reliably unbind() when the dialog fragment is closed/dismissed/recreating itself, otherwise I am going to leak it because of the ServiceConnection (right?). I don’t know when to do it?. onDismiss, onDestroyView, onSaveInstanceState ... I have tried many options but I often end up with a IllegalArgumentException saying that the service connection is not registered.

  • The service might never reach the completeSecondTask() part, and so no one is going to stop it, leaking it for no reason. I should probably call stopService() somewhere, but where? These are different scenarios:

    1. I force-quit the app / recreate the fragment after a completeSecondTask() call: the Service should keep going until it ends.

    2. I recreate the fragment without having called completeSecondTask() : the Service should keep going until it ends. (There’s proper logic inside my fragment to handle this)

    3. I force-quit the app without having called completeSecondTask() : the Service should stop.

Questions

Now, you might see this as two questions: how to handle unbind(), and how to handle stopService().

However, because I am finding so hard to set up this little task, I am thinking that this is deeply flawed and I should use a totally different approach. I hope you can shed some light on this for me.

0 Answers
Related