tags:

views:

28

answers:

1

I have a Service that downloads a file from the internet. What I want is for the response (an InputStream in this case) to be handled by a custom handler that can be switched (like a strategy pattern) but I can't figure out how to do this.

So basically the User of the API would be able to plug in different handlers for the response, some would parse XML, others might save files etc.

I realise I could pass through the activity context and execute the method from this (given some interface) but I don't want to do this obviously, in case the Activity is closed in the meantime while the file is still downloaded.

How might I do this?

UPDATE - I just had one idea it will work sort of but has problem if the DownloadRunnable gets changed between executions of the downloads.

The modified Runnable interface

interface DownloadRunnable {
    void run(InputStream stream);
}

An enum

public enum ServiceHandler {
    DOWNLOAD_HANDLER;

    private DownloadRunnable handler=null;

    /**Called from the Service*/
    public void doHandler(InputStream stream) {
        if(handler==null) throw new IllegalStateException("You must set the handler first: setDownloadHandler()");
        this.handler.run(stream);
    }

     /**Set before theservice runs*/
     public void setHandler(DownloadRunnable r) {
         this.handler = r;
     }
}

The client can set what happens for each enum

ServiceHandler.DOWNLOAD_HANDLER.setHandler(new DownloadRunnable() {

        @Override
        public void run(InputStream stream) {
            // The user can do anything they want with the stream
        }
    });

The Service runs this when the input stream is available

ServiceHandler.DOWNLOAD_HANDLER.doHandler(stream);

This kind of works but is not so elegant.

  1. What if the user runs the same operation twice with different DownloadRunnables set (before the first is complete)

  2. The user has to set the Handler first is a separate enum object (not so user friendly or intuitive).

  3. Maybe I could create a Map of DownloadRunnables each with an ID (static field - auto increment) that gets passed back and the correct runnable can then get executed...but all this is getting more complex than I had hoped.

+1  A: 

Step #1: Write an interface describing the strategy API

Step #2: Write implementations of the interface for each strategy pattern

Step #3: Have the Activity either pass in a strategy pattern instance or some identifier of the pattern to use (so the Service creates the object) when it requests the download

This seems too simple. I think I am missing the point of your question.

CommonsWare
but I can't pass objects to the Service unless they are Parceable - so how do I pass the strategy
jax
"but I can't pass objects to the Service unless they are Parceable" Sure you can. Only a remote service would prohibit this, and in that case, it is utterly impossible for your clients to supply the strategy, anyway.
CommonsWare
Why is it impossible in this case because in your answer you said it was easy?
jax
@jax My original answer was for a local service. Very very few people write remote services. You neglected to specify in your question whether this was for a local or remote service, so I assumed it was for a local service. If you are indeed implementing a remote service, let me know and I will delete this answer.
CommonsWare
sorry, your original inclination was correct. It is a local service extending the Android Service class.
jax
@jax This means you are not limited to `Parcelable` objects, since you should not be using AIDL. The local binding pattern allows you to pass arbitrary objects from activity to service, since they are all in the same JVM.
CommonsWare
Is there any way to do this without having the client bind to the service?
jax
@jax Binding is less risky than your alternatives (e.g., singleton).
CommonsWare