Android Volley - How to isolate requests in another class

Hi I'd like to modularize the volley requests so I don't mix activity presentation code with volley requests. All samples I saw, the volley request are being placed -for example- on the OnClick event from an activity button.

I mean this code(taken from diff source):

// prepare the Request
JsonObjectRequest getRequest = new JsonObjectRequest(Request.Method.GET, url, null,
    new Response.Listener<JSONObject>() 
    {
        @Override
        public void onResponse(JSONObject response) {   
                        // display response     
            Log.d("Response", response.toString());
        }
    }, 
    new Response.ErrorListener() 
    {
         @Override
         public void onErrorResponse(VolleyError error) {            
            Log.d("Error.Response", response);
       }
    }
);

// add it to the RequestQueue   
queue.add(getRequest);

My point here is how to get this all request code to another class and just instance the class and call the makeRequest. I already tried this but it fails. I don't know if it's something related with the Context but it fails...

I did this:

public void onClick(View v) {
    try{

        Utils varRequest = new Utils(getApplicationContext());
        String url = "https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=";

        varRequest.makeRequest(url);
        mitexto.setText(varRequest.miError);
    }
    catch(Exception excepcion) {
        System.out.println(excepcion.toString());

        }

    }

... and the Utils class is:

public class Utils {
    public Context contexto;
    public String miError;
    private RequestQueue queue ;

    public Utils (Context contextoInstancia){
        contexto = contextoInstancia;
        queue = Volley.newRequestQueue(contexto);
    }

    public void makeRequest(String url){

        JsonObjectRequest jsObjRequest = new JsonObjectRequest(Request.Method.GET, url, null, new Response.Listener<JSONObject>() {

            @Override
            public void onResponse(JSONObject response) {
                // TODO Auto-generated method stub
                miError="Response => "+response.toString();
            }
        }, new Response.ErrorListener() {

            @Override
            public void onErrorResponse(VolleyError error) {
                // TODO Auto-generated method stub
                miError="Response => "+error.networkResponse.toString();
            }
        });

        queue.add(jsObjRequest);
    }
}   

Can anyone tell me what I'm doing wrong, or how to structure the code?

Thanks in advance.


Solution 1:

in general it's good practice to seperate this sort of stuff, so you're on the right path, consider making a singelton class that handles your requests - this is a very general template, but should get your structure going:

create a singleton class, which you instatiate when you're application comes up:

public class NetworkManager
{
    private static final String TAG = "NetworkManager";
    private static NetworkManager instance = null;

    private static final String prefixURL = "http://some/url/prefix/";

    //for Volley API
    public RequestQueue requestQueue;

    private NetworkManager(Context context)
    {
        requestQueue = Volley.newRequestQueue(context.getApplicationContext());
        //other stuf if you need
    }

    public static synchronized NetworkManager getInstance(Context context)
    {
        if (null == instance)
            instance = new NetworkManager(context);
        return instance;
    }

    //this is so you don't need to pass context each time
    public static synchronized NetworkManager getInstance()
    {
        if (null == instance)
        {
            throw new IllegalStateException(NetworkManager.class.getSimpleName() +
                    " is not initialized, call getInstance(...) first");
        }
        return instance;
    }

    public void somePostRequestReturningString(Object param1, final SomeCustomListener<String> listener)
    {

        String url = prefixURL + "this/request/suffix";

        Map<String, Object> jsonParams = new HashMap<>();
        jsonParams.put("param1", param1);

        JsonObjectRequest request = new JsonObjectRequest(Request.Method.POST, url, new JSONObject(jsonParams),
                new Response.Listener<JSONObject>()
                {
                    @Override
                    public void onResponse(JSONObject response)
                    {
                         Log.d(TAG + ": ", "somePostRequest Response : " + response.toString());
                         if(null != response.toString())
                           listener.getResult(response.toString());
                    }
                },
                new Response.ErrorListener()
                {
                    @Override
                    public void onErrorResponse(VolleyError error)
                    {
                        if (null != error.networkResponse)
                        {
                            Log.d(TAG + ": ", "Error Response code: " + error.networkResponse.statusCode);
                            listener.getResult(false);
                        }
                    }
                });

        requestQueue.add(request);
    }
}

when your application comes up:

public class MyApplication extends Application
{
  //...

    @Override
    public void onCreate()
    {
        super.onCreate();
        NetworkManager.getInstance(this);
    }

 //...

}

a simple listener interface for your callback (seperate file would do good):

public interface SomeCustomListener<T>
{
    public void getResult(T object);
}

and finally, from wherever you want, the context is already in there, just call:

public class BlaBla
{
    //.....

        public void someMethod()
        {
            NetworkManager.getInstance().somePostRequestReturningString(someObject, new SomeCustomListener<String>()
            {
                @Override
                public void getResult(String result)
                {
                    if (!result.isEmpty())
                    {
                     //do what you need with the result...
                    }
                }
            });
        }
}

you can use any object with the listener, depending on what you need to receive, this also works for GET requests with some minor modification, (see this SO thread for more about GET) and you can call that from everywhere (onClicks, etc.), just remember they need to match between methods.

Hope this Helps and not too late!

Solution 2:

I use a simple solution for this. Create Listeners (since they are interface, they can't be instantiated directly but they can be instantiated as an anonymous classes that implement the interface) inside the activity or fragment. Pass these instances as parameters to the Request.(StringRequest, JsonObjectRequest, or ImageRequest).

public class MainActivity extends Activity {

private static final String URI_WEATHER = "http://marsweather.ingenology.com/v1/latest/";

private Listener<JSONObject> listenerResponse = new Listener<JSONObject>() {

    @Override
    public void onResponse(JSONObject response) {
        Toast.makeText(MainActivity.this, "Resonse " + response.toString(), Toast.LENGTH_LONG).show();
    }
};

private ErrorListener listenerError = new ErrorListener() {

    @Override
    public void onErrorResponse(VolleyError error) {
        Toast.makeText(MainActivity.this, "Error " + error, Toast.LENGTH_LONG).show();

    }
};

}

Next, create a class that has request and pass this listeners to this class' request method , that's all. I don't explain this part this is same as creating a request object in any tutorials.But you can customize this class as you wish. You can create singleton RequestQueue to check priority, or set body http body parameters to this methods as paremeters.

public class NetworkManagerWeather {

public static void getWeatherData(int method, Context context, String url,
        Listener<JSONObject> listenerResponse, ErrorListener listenerError) {
    JsonObjectRequest requestWeather = new JsonObjectRequest(Method.GET, url, null, listenerResponse,
            listenerError);
    Volley.newRequestQueue(context).add(requestWeather);
}

}

Finally invoke this method from MainActivity for instatnce

NetworkManagerWeather.getWeatherData(Method.GET, this, URI_WEATHER, listenerResponse, listenerError);