user3092616 user3092616 - 1 month ago 9
Android Question

Async task using generics for different return types from doInBackground

I have a utility class that extends Async task. I will be using this call to make HTTP requests in the background but I will also have more specialized sub classes of this that prepare the parameters, headers, url to call, so I can remove more common work from the GUI.

The issue is that I want to make use of Generics. The base API class doInBackground will return a string, there's a more specialized Json subclass that will call parent and return a JSONObject and do some parsing of the json response, there's specialized classes that extend the Json subclass and return List of custom objects, and so on. The reason for this is if we need to swap in XML and XML processing the specialized sub classes will have both a JSON and XML implementation. This is because we are re-using for a couple different api's overall.

So I tried playing around with Generics but I'm not 100% sure I understand the implementation in this case. It's obvious when you want to do things like List and make a list of List but how do I apply it here? I think I'm mainly confused about mocking up the code vs implementation, will everything just be T in the base and subclasses, than when I instantiate instances somewhere else like in the GUI that's when I specify the type of return I expect? Than I think I understand. So what I'm saying is when writing up the classes I only use T, never specify a Type and in the code where I instantiate instances that's when I specify a type and that's what the return type of doInBackground will be?

I also want to be able to implement onPostExecute() generically because I will use a callback setup so the GUI can easily subscribe to when the call is finished and process the result, but the interfact will also have a generic for the onPostExecute(T response). So I can create new instances, pass 'this', and when the async task is finished it will call the callback with the result and the callback can handle the appropriate type.

public class Base<T> extends AsyncTask<String, Integer, T>
{
protected Callback callback = null; //interface implemented for processing response

public Base setCallback(Callback callback){ this.callback = callback; return this; }

@Override
protected T doInBackground(String... uri)
{
//do http call
String response = "";
return response; //raw string of server response
}

@Override
final protected void onPostExecute(T result)
{
//no overrides, same every time
if( callback != null )
{
callback.finished(result); //forward generic result, but there it will be typed
}
}

public class JsonBase<T> extends Base<T>
{
@Override
protected T doInBackground(String... uri)
{
//this will be a JSONObject returned
String result = (String)super.dpInBackground(uri); //gives me back a string
return new JSONObject(result); //return a json object
}
}

public class SpecializedBase<T> extends JsonBase<T>
{
@Override
protected T doInBackground(String... uri)
{
//this will be a List<String> returned
//iterate over all json array strings and pass back
return new List<String>();
}
}

class FragmentFoo extends Fragment implements Callback
{
@Override
protected void onViewCreate(...)
{
//Example usage
new JsonBase< JSONObject >().setCallback(this).execute("<url">);
new SpecializedBase< List<String> >().setCallback(this).execute(""); //hard coded internally for example

}


//Can we do something like this?
@Override
protected void finished(JSONObject object)
{
//handle json response
}

@Override
protected void finished(List<String> strings)
{
//handle list of strings response
}
}

interface Callback
{
public <T> void finish(T response);
}


The specialized sub classes of Async will be tailored to specific types, and return different types, and we want to handle those specialized type depending on where we are in the GUI and what we're doing. Otherwise all we can do is all the logic in the GUI or have another middle layer of wrappers...This is all just a primitive example illustrating my point and how we want this to work.

Answer

Just kept T and anytime it complained about casting (T)response I just added a suppress warning. As long as I know what to expect in the specific callback and cast to that type, it's fine. But could easily crash at runtime if I make a mistake and cast it to something else.

It compiles, runs, and works. But doesn't seem like a clean appropriate solution.

Comments