Tuesday, July 21, 2015

EventBus with Volley in Android Application

Standard
Hello:

Developing De-coupled applications always helps in maintainability of the project. In android application development we use listener pattern using interfaces to handle callback. Though it gets the work done but with bigger projects this may result into maintainability issues.

In this post I will explain how to use Android Volley with EventBus without interface listeners.

Volley: This library makes network calls and caching very easy. More info can be found here.
EventBus: This tiny library helps in implementing subscriber-publisher model with ease. More info can be found here.

Let us start.

  1. We will create a blank Project using Android Studio
  2. In the build.gradle add following dependencies

        compile 'com.android.support:appcompat-v7:22.2.0'
        compile 'com.mcxiaoke.volley:library:1.0.17'
        compile 'de.greenrobot:eventbus:2.4.0'
        compile 'com.google.code.gson:gson:2.3.1'
    

  3. Create class VolleyManager.java. This is singleton class for Volley.
    public class VolleyManager {
    
        private static VolleyManager instance;
        private RequestQueue mRequestQueue;
    
        public synchronized static void initialize(Context context) {
            if (instance == null) {
                instance = new VolleyManager();
                instance.mRequestQueue = Volley.newRequestQueue(context);
            }
        }
    
    
        public synchronized static VolleyManager getInstance() {
            return instance;
        }
    
        public RequestQueue getRequestQueue() {
            return mRequestQueue;
        }
    }
    

  4. BaseApplication to extend Applicaton class.
    public class BaseApplication extends Application {
    
        @Override
        public void onCreate() {
            super.onCreate();
            VolleyManager.initialize(this);
        }
    }
    

  5. Create class HTTPRequestManager.java. This class will responsible for making network call using Volley
    public class HTTPRequestManager<P extends BaseSuccessEvent, Q extends BaseErrorEvent> {
    
        public static final String TAG = HTTPRequestManager.class.getSimpleName();
        public boolean isBaseActivity;
        ProgressDialog progressDialog;
        int responseCode;
        ErrorData errorData;
        Response.Listener<JSONObject> jsonResponseListener;
        Response.ErrorListener errorListener;
        EventBus eventBus = EventBus.getDefault();
        private HashMap<String, String> params;
        private HashMap<String, String> headers;
        private String url;
        private Context context;
        private Class<P> pClass;
        private Class<Q> qClass;
    
        public HTTPRequestManager(Context context, String url, Class<P> pClass, Class<Q> qClass) {
            this.context = context;
            this.url = url;
            params = new HashMap<>();
            headers = new HashMap<>();
            addJSONHeaders();
            errorData = new ErrorData();
            isBaseActivity = true;
            this.pClass = pClass;
            this.qClass = qClass;
            setListeners();
    
        }
    
        private void addJSONHeaders() {
            AddHeader("Accept", "application/json");
            AddHeader("Content-type", "application/json");
        }
    
        private void setListeners() {
            jsonResponseListener = new Response.Listener<JSONObject>() {
                @Override
                public void onResponse(JSONObject response) {
                    dismissProgressDialog();
                    try {
                        P res = pClass.newInstance();
                        res.setResponse(response.toString());
                        eventBus.post(res);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            };
            errorListener = new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    dismissProgressDialog();
                    try {
                        Q err = qClass.newInstance();
                        ErrorData errorData = new ErrorData();
                        errorData.setMessage(error.toString());
                        err.setErrorData(errorData);
                        eventBus.post(err);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            };
    
        }
    
    
        public HashMap<String, String> getParams() {
            return params;
        }
    
        public HashMap<String, String> getHeaders() {
            return headers;
        }
    
    
        public String getCompleteUrl() {
            return url;
        }
    
        public int getResponseCode() {
            return responseCode;
        }
    
        public ErrorData getErrorData() {
            return errorData;
        }
    
        public void execute() {
            progressDialog = new ProgressDialog(context);
            progressDialog.setCancelable(false);
            dismissProgressDialog();
            progressDialog.show();
            addToRequestQueue(getJsonRequest());
        }
    
    
        public void dismissProgressDialog() {
            if (progressDialog != null && progressDialog.isShowing()) {
                progressDialog.dismiss();
            }
        }
    
        private <X> void addToRequestQueue(Request<X> req, String tag) {
            req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);
            VolleyManager.getInstance().getRequestQueue().add(req);
        }
    
        private <X> void addToRequestQueue(Request<X> req) {
            addToRequestQueue(req, "");
        }
    
    
        public void AddParam(String name, String value) {
            params.put(name, value);
        }
    
        public void AddHeader(String name, String value) {
            headers.put(name, value);
        }
    
        Request<JSONObject> getJsonRequest() {
            try {
                getUrl();
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
    
            return new JsonObjectRequest(Request.Method.GET, url, jsonResponseListener, errorListener) {
                @Override
                public Map<String, String> getHeaders() throws AuthFailureError {
                    return headers;
                }
    
                @Override
                protected Map<String, String> getParams() {
                    return params;
                }
            };
        }
    
    
        void getUrl() throws UnsupportedEncodingException {
            String combinedParams = "";
            if (!params.isEmpty()) {
                combinedParams += "?";
                for (Map.Entry<String, String> entry : params.entrySet()) {
                    String paramString = entry.getKey() + "="
                            + URLEncoder.encode(entry.getValue(), "UTF-8");
                    if (combinedParams.length() > 1) {
                        combinedParams += "&" + paramString;
                    } else {
                        combinedParams += paramString;
                    }
                }
            }
            url = url + combinedParams;
        }
    }
    
  6. Because of Type Erasure in Java we can't use generic events with EventBus.
    We will create BaseEvents for Success and Error and then extend them for specific events
    BaseSuccessEvent
    public class BaseSuccessEvent {
    
        private String response;
    
        public String getResponse() {
            return response;
        }
    
        public void setResponse(String response) {
            this.response = response;
        }
    
        public <T> T getTypedResponse(Class<T> tClass) throws ClassNotFoundException {
            return Json.deSerialize(response, tClass);
        }
    }
    
    

    BaseErrorEvent
    public class BaseErrorEvent {
        private ErrorData errorData;
    
        public ErrorData getErrorData() {
            return errorData;
        }
    
        public void setErrorData(ErrorData errorData) {
            this.errorData = errorData;
        }
    
    }
    

  7. Now all set to use these In the activity where we want to make network calls, code will look this
    public class MainActivity extends ActionBarActivity {
    
        static String TAG = "VolleyEventBus";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            EventBus.getDefault().register(this);
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            EventBus.getDefault().unregister(this);
        }
    
        @EventBusHook
        public void onEvent(GithubUserSuccessEvent githubUserSuccessEvent) {
            Toast.makeText(this, "Success", Toast.LENGTH_SHORT).show();
            Log.d(TAG, githubUserSuccessEvent.getResponse());
        }
    
        @EventBusHook
        public void onEvent(GithubUserErrorEvent githubUserErrorEvent) {
            Toast.makeText(this, "Error occurred.", Toast.LENGTH_SHORT).show();
            Log.d(TAG, githubUserErrorEvent.getErrorData().getMessage());
        }
    
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            // Inflate the menu; this adds items to the action bar if it is present.
            getMenuInflater().inflate(R.menu.menu_main, menu);
    
            Button requests = (Button) findViewById(R.id.requests);
            requests.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    HTTPRequestManager<GithubUserSuccessEvent, GithubUserErrorEvent> request = new HTTPRequestManager<>(MainActivity.this, "https://api.github.com/users/ashwanikumar04", GithubUserSuccessEvent.class, GithubUserErrorEvent.class);
                    request.execute();
                }
            });
    
            return true;
        }
    
        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            // Handle action bar item clicks here. The action bar will
            // automatically handle clicks on the Home/Up button, so long
            // as you specify a parent activity in AndroidManifest.xml.
            int id = item.getItemId();
    
            //noinspection SimplifiableIfStatement
            if (id == R.id.action_settings) {
                return true;
            }
    
            return super.onOptionsItemSelected(item);
        }
    }
    
    

Full working code can be downloaded from here.
Hope this helps. Happy coding.
Thanks for printing this post. Hope you liked it.
Keep visiting and sharing.
Thanks,
Ashwani.

5 comments :

  1. If i want to make more than one http request in single activity or fragment then how i can handle this on this pattern

    ReplyDelete
    Replies
    1. Hi,
      You can make as many calls you want. You will have different event for each call, thus eventbus will call corresponding method.

      Delete
  2. I'm stuck :/
    I'm a beginner so sorry about (for you) trivial question).
    From where did you get ErrorData type ? I've copied all to my project and I have this in red :/
    and also in BaseSuccessEvent.class you return Json.deSerialize(response, tClass); Json is also in red, android studio cannot resolve symbol

    my gradle deps are a bit different than your
    compile 'com.android.support:appcompat-v7:23.4.0'
    compile 'com.android.volley:volley:1.0.0'
    compile 'org.greenrobot:eventbus:3.0.0'
    compile 'com.google.code.gson:gson:2.4'

    ReplyDelete
    Replies
    1. Json is a helper class: https://github.com/ashwanikumar04/volleyeventbus/blob/master/app/src/main/java/ashwani/in/volleyeventbus/utils/Json.java

      ErrorData: https://github.com/ashwanikumar04/volleyeventbus/blob/master/app/src/main/java/ashwani/in/volleyeventbus/entities/ErrorData.java

      Delete
  3. VolleyManager.getInstance().getRequestQueue().add(req);


    Se puede arreglar con :

    public class VolleyManager{


    private static VolleyManager mInstance;
    private static Context mCtx;
    private RequestQueue mRequestQueue;

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



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


    public RequestQueue getRequestQueue() {
    if (mRequestQueue == null) {
    Cache cache = new DiskBasedCache(mCtx.getCacheDir(), 10 * 1024 * 1024);
    Network network = new BasicNetwork(new HurlStack());
    mRequestQueue = new RequestQueue(cache, network);
    mRequestQueue.start();
    }
    return mRequestQueue;
    }
    }

    ReplyDelete