How to create a proper Volley Listener for cross class Volley method calling
Asked Answered
T

3

9

I aim to call Volley from another class in, a very succinct, modular way ie:

            VolleyListener newListener = new VolleyListener();
            VolleySingleton.getsInstance().somePostRequestReturningString(getApplicationContext(), newListener);
            JSONObject data = newListener.getResponse();

But am having allot of trouble getting the listener portion to work so as to be able to access the resulting data from a method such as

newListener.getResponse();

There are a few questions on this site that generally outline how to set up a volley call from another class, such as: Android Volley - How to isolate requests in another class. I have had success getting the method call to work, but to now get that data into the present class for usage has caused trouble.

I have the action within my VolleySingleton class as:

public void somePostRequestReturningString(final Context context,final VolleyListener<String> listener) {

        final String URL = "http://httpbin.org/ip";

        JsonObjectRequest set = new JsonObjectRequest(Request.Method.GET, URL, ((String) null),
                new Response.Listener<JSONObject>() {
                    @Override
                    public void onResponse(JSONObject response) {

                        listener.outPut = response.toString();
                        //Toast.makeText(context, response.toString(), Toast.LENGTH_LONG).show();
                    }
                },
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        Log.d("Error.Response", error.toString());
                    }
                }
        );

        mRequestQueue.add(set);
}

and within the listener class:

public class VolleyListener {
    public static String outPut;

    private static Response.Listener<String> createSuccessListener() {
        return new Response.Listener<String>() {
            @Override
            public void onResponse(String response) {
                outPut = response;
            }
        };
    }
}

How can I configure this to work and allow Volley calls and data retrieval from another class, particularly how to build callbacks correctly?

Tyr answered 5/11, 2015 at 2:4 Comment(0)
M
22

For your requirement, I suggest you refer to my following solution, hope it's clear and helpful:

First is the interface:

public interface VolleyResponseListener {
    void onError(String message);

    void onResponse(Object response);
}

Then inside your helper class (I name it VolleyUtils class):

public static void makeJsonObjectRequest(Context context, String url, final VolleyResponseListener listener) {
        JsonObjectRequest jsonObjectRequest = new JsonObjectRequest
                (url, null, new Response.Listener<JSONObject>() {

                @Override
                public void onResponse(JSONObject response) {
                    listener.onResponse(response);
                }
            }, new Response.ErrorListener() {

                @Override
                public void onErrorResponse(VolleyError error) {
                    listener.onError(error.toString());
                }
            }) {

        @Override
        protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
            try {
                String jsonString = new String(response.data,
                        HttpHeaderParser.parseCharset(response.headers, PROTOCOL_CHARSET));
                return Response.success(new JSONObject(jsonString),
                        HttpHeaderParser.parseCacheHeaders(response));
            } catch (UnsupportedEncodingException e) {
                return Response.error(new ParseError(e));
            } catch (JSONException je) {
                return Response.error(new ParseError(je));
            }
        }
    };

    // Access the RequestQueue through singleton class.
    VolleySingleton.getInstance(context).addToRequestQueue(jsonObjectRequest);
}

Then, inside your Activity classes, you can call like the following:

VolleyUtils.makeJsonObjectRequest(mContext, url, new VolleyResponseListener() {
        @Override
        public void onError(String message) {

        }

        @Override
        public void onResponse(Object response) {

        }
    });

You can refer to the following questions for more information (as I told you yesterday):

Android: How to return async JSONObject from method using Volley?

POST Request Json file passing String and wait for the response Volley

Android/Java: how to delay return in a method

Matri answered 5/11, 2015 at 2:16 Comment(2)
What I dislike is base Volley syntax, is almost identical to what you are placing within the Activity class in your example. I wish to get away from the verbose nature of it and streamline. If that is not possible/too much effort to be truly worth it, I may simply just move on. What do you think?Tyr
In my 3rd link, you will find "Utils.makeJsonObjectRequest_Callable", I don't know if it is what you like. If not, I think there is no more beautiful syntax for you. Or you can read here #16905241Matri
M
7

Volley excels at RPC-type operations used to populate a UI, such as fetching a page of search results as structured data. It integrates easily with any protocol and comes out of the box with support for raw strings, images, and JSON. By providing built-in support for the features you need, Volley frees you from writing boilerplate code and allows you to concentrate on the logic that is specific to your app.

How to create Common GET/POST Method Using Volley .

Create a Application Class

The Application class in Android is the base class within an Android app that contains all other components such as activities and services

    public class MyApplication extends Application {

    public static final String TAG = MyApplication.class
            .getSimpleName();

    private RequestQueue mRequestQueue;

    private static MyApplication mInstance;

    @Override
    public void onCreate() {
        super.onCreate();
        mInstance = this;
    }

    public static synchronized MyApplication getInstance() {
        return mInstance;
    }

    public RequestQueue getRequestQueue() {
        if (mRequestQueue == null) {
            mRequestQueue = Volley.newRequestQueue(getApplicationContext());
        }

        return mRequestQueue;
    }

    public <T> void addToRequestQueue(Request<T> req, String tag) {
        // set the default tag if tag is empty
        req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);
        getRequestQueue().add(req);
    }

    public <T> void addToRequestQueue(Request<T> req) {
        req.setTag(TAG);
        getRequestQueue().add(req);
    }

    public void cancelPendingRequests(Object tag) {
        if (mRequestQueue != null) {
            mRequestQueue.cancelAll(tag);
        }
    }
}

Make Sure you add this Manifest Section .

 <application
    .....
    android:name=".MyApplication"
    >

Now, You need to create Singleton Class .

Singleton Pattern says that just define a class that has only one instance and provides a global point of access to it .

   public class MySingleton
  {
    private static MySingleton mInstance;
    private RequestQueue mRequestQueue;
    private static Context mCtx;

    private MySingleton(Context context)
    {
        mCtx = context;
        mRequestQueue = getRequestQueue();
    }

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

    public RequestQueue getRequestQueue()
    {
        if (mRequestQueue == null)
        {
            mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext());
        }
        return mRequestQueue;
    }

    public <T> void addToRequestQueue(Request<T> req)
    {
        getRequestQueue().add(req);
    }

 }

Now Common Class

 public class VolleyUtils {

    public static void GET_METHOD(Context context, String url, final VolleyResponseListener listener)
    {

        // Initialize a new StringRequest
        StringRequest stringRequest = new StringRequest(
                Request.Method.GET,
                url,
                new Response.Listener<String>() {
                    @Override
                    public void onResponse(String response) {
                        listener.onResponse(response);

                    }
                },
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        listener.onError(error.toString());

                    }
                })

        {


        };

        // Access the RequestQueue through singleton class.
        MySingleton.getInstance(context).addToRequestQueue(stringRequest);
    }

    public static void POST_METHOD(Context context, String url,final Map<String,String> getParams, final VolleyResponseListener listener)
    {

        // Initialize a new StringRequest
        StringRequest stringRequest = new StringRequest(
                Request.Method.POST,
                url,
                new Response.Listener<String>() {
                    @Override
                    public void onResponse(String response) {
                        listener.onResponse(response);

                    }
                },
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        listener.onError(error.toString());

                    }
                })

        {

            /**
             * Passing some request headers
             * */
            @Override
            public Map<String, String> getHeaders() throws AuthFailureError {
                HashMap<String, String> headers = new HashMap<String, String>();
                getParams.put("Content-Type", "application/json; charset=utf-8");
                return headers;
            }

        };

        // Access the RequestQueue through singleton class.
        MySingleton.getInstance(context).addToRequestQueue(stringRequest);
    }
}

Now You should create Interface .

A class implements an interface, thereby inheriting the abstract methods of the interface .

 /**
 * Created by Intellij Amiyo  on 10-06-2017.
 * Please follow standard Java coding conventions.
 * http://source.android.com/source/code-style.html
 */
public interface VolleyResponseListener {

    void onError(String message);

    void onResponse(Object response);
}

How To Call

public void _loadAPI()
{
    //GET 
    String URL_GET = "";
    VolleyUtils.GET_METHOD(MainActivity.this, URL_GET, new VolleyResponseListener() {
        @Override
        public void onError(String message) {
            System.out.println("Error" + message);
        }

        @Override
        public void onResponse(Object response) {

            System.out.println("SUCCESS" + response);
        }
    });


    //POST
    String URL_POST=" ";
    VolleyUtils.POST_METHOD(MainActivity.this, URL_POST,getParams(), new VolleyResponseListener() {
        @Override
        public void onError(String message) {
            System.out.println("Error" + message);
        }

        @Override
        public void onResponse(Object response) {

            System.out.println("SUCCESS" + response);
        }
    });


}

public Map<String,String> getParams()
{
    Map<String, String> params = new HashMap<String, String>();
    params.put("YOUR_KEY", "VALUE");
    return params;
}

For demo you should Download Volley-Common-Method

Mundy answered 10/6, 2017 at 7:38 Comment(0)
A
1

If you followed the general example from Android Volley - How to isolate requests in another class, (including the stuff regarding the singleton stuff) and looking for the parsing part (or, how to actually use the objects you receive), then this is the (again very general) addition

say you have a Json object coming in, that looks somewhat like this :

{"users": [{"username":"Jon Doe","userid":83}, {"username":"Jane Doe",userid":84}]}

and our User object would look something like this:

public class User
{
 String username;
 int userid;

 public String getName()
 {
 return username;
 }

 public int getId()
 {
 return userid;
 }
}

Important: When working with Gson (you will see later), the object fields should be named according to params you get in the Json, this sort of reflection is how the parsing works.

then, the request itself would look something like this (note the listener callback returning a

List<User>

object back to the caller, you'll see later):

public class NetworkManager
{

 //... other stuff

 public void getUsers(final SomeCustomListener<List<User>> listener)
 {
    final String URL = "http://httpbin.org/ip";

    StringRequest request = new StringRequest(Request.Method.GET, url,
            new Response.Listener<String>()
            {
                @Override
                public void onResponse(String response)
                {
                    Log.d(TAG + ": ", "getUsers Response: " + response);
                    List<User> users = MyJsonParser.getListObjects(response, "$.users[*]", User.class);

                    if(null != users)
                      listener.getResult(users);
                }
            },
            new Response.ErrorListener()
            {
                @Override
                public void onErrorResponse(VolleyError error)
                {
                    if (null != error.networkResponse)
                    {
                        Log.d(TAG + ": ", "Error Response code: " + error.networkResponse.statusCode);
                        listener.getResult(null);
                    }
                }
            });
    requestQueue.add(request);

 // ... other stuff
}

what you would need now is that class to parse the Json string, namely the object list, in this example I use Gson (again - this is a general example, change and reorder stuff according to your needs, you could probably also optimize this some more - it's just for the explanation):

public class MyJsonParser
{
 //... other stuff
 public static <T> List<T> getListObjects(String json_text, String json_path, Class<T> c)
 {
    Gson gson = new Gson();
    try
    {
        List<T> parsed_list = new ArrayList<>();
        List<Object> nodes = JsonPath.read(json_text, json_path);

        for (Object node : nodes)
        {
            parsed_list.add(gson.fromJson(node.toString(), c));
        }
        return (parsed_list);
    }
    catch (Exception e)
    {
        return (new ArrayList<>());
    }
 }
 //... other stuff
}

So, after we have all this (and the following stuff from the pre-mentioned SO question), what you said you were looking for is the callback in your working code, well that can be achieved in a couple of ways:

A straight forward way:

just call the method and override it's callback right there, e.g:

public class SomeClass
{

 private List<User> mUsers;

 private void someMethod()
 {
  // ... method does some stuff

    NetworkManager.getInstance().getUsers(new SomeCustomListener<List<User>>()
    {
        @Override
        public void getResult(List<User> all_users)
        {
            if (null != allUsers)
            {
                mUsers = allUsers;
                // ...  do other stuff with our info
            }
        }
    });

   // ... method does some more stuff
  }
}

Or, in an indirect way (considering the time, memory consumption, etc. ), you can save the info you got in the same Singelton (or another container), and create a get method for it, and just get the object later (looks more slick)

remember: fire the request before (considering the latency for the response), as the nature of these callbacks is to be dependent on the response which might be delayed.

It would then look like this:

private List<User> mUsers;

private void someMethod()
{
 // ... method does some stuff

 mUsers = NetworkManager.getInstance().getUsersObject();

 // ... method does some more stuff
}

A different option entirely would be to consider using Retrofit, that does the parsing for you, uses annotations, and is supposedly a lot faster , that might be what you're looking for (for the streamlined look) - I would read up on benchmarks, especially since the new 2.0 version came out.

Hope this Helps (although somewhat late)! :)

Alejandraalejandrina answered 24/11, 2015 at 10:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.