Posts

About

Clean Android Networking Code Part 1

June 30, 2015

Introduction

Are you tired of writing Android networking code that looks similar to what is shown below? Networking code like this violates the SRP and is usually coupled to activities or fragments. This post explores a way to simplify and decouple networking code from activities using Volley and EventBus.

public class MainActivity extends AppCompatActivity {

    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        textView = (TextView) findViewById(R.id.text);
        new GoogleRequestTask().execute();
    }

    class GoogleRequestTask extends AsyncTask<Void, Void, String> {

        @Override
        protected String doInBackground(Void... params) {
            BufferedInputStream in = null;
            String data = "";
            try {
                URL url = new URL("http://www.google.com");
                URLConnection urlConnection = (HttpURLConnection) url.openConnection();
                in = new BufferedInputStream(urlConnection.getInputStream());
                data = readStream(in);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                closeStream(in);
            }
            return data;
        }

        @Override
        protected void onPostExecute(String output) {
            textView.setText(output);
        }

        private void closeStream(BufferedInputStream in) {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        private String readStream(BufferedInputStream in) throws IOException {
            byte[] contents = new byte[1024];
            int bytesRead = 0;
            String strFileContents = "";
            while ((bytesRead = in.read(contents)) != -1) {
                strFileContents = new String(contents, 0, bytesRead);
                System.out.print(strFileContents);
            }
            return strFileContents;
        }
    }
}

Cleaner networking code with Volley

Volley is a library created by Google to make networking for Android apps easier and faster. It takes care of scheduling and executing network requests. It also has the ability to cancel requests whenever, ex. when an activity is stopped. Some other notable features that won’t be covered in this post are caching (memory and disk) and request prioritization.

Volley can be added to a project using gradle with the following:

compile 'com.mcxiaoke.volley:library:1.0.15'

Please note that the volley link is to an unofficial mirror of the Android Volley library. If you would like to include it directly take a look at this tutorial

Volley comes out of the box with several different request types, and it is easy to make custom ones. When creating a request the request type, url, a success callback, and an error callback must be provided. These requests must be added to a request queue that handles making it. With Volley the complex network code above can be transformed into a much simpler version shown below.

public class MainActivity extends AppCompatActivity {

    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        textView = (TextView) findViewById(R.id.text);
        makeRequest();
    }

    private void makeRequest() {
        RequestQueue queue = Volley.newRequestQueue(this);
        String url = "http://www.google.com";
        StringRequest stringRequest = new StringRequest(
                Request.Method.GET,
                url,
                new Response.Listener<String>() {
                    @Override
                    public void onResponse(String response) {
                        textView.setText(response);
                    }
                },
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        textView.setText("That didn't work!");
                    }
                });
        queue.add(stringRequest);
    }
}

Setting up a global request queue

To have multiple concurrent requests going at once in an app then it is a good idea to have a single request queue to manage them all. This can be accomplished by creating the a RequestQueue singleton and modifying the activity like below. When creating a request in volley a tag can be added to it. This tag is used to cancel requests which can also be seen below.

WebRequestQueue.java

public class WebRequestQueue {
    private static WebRequestQueue webRequestQueue;
    private RequestQueue volleyRequestQueue;

    private WebRequestQueue(Context context) {
        volleyRequestQueue = Volley.newRequestQueue(context.getApplicationContext());
    }

    public static WebRequestQueue getInstance(Context context) {
        if (webRequestQueue == null) {
            synchronized (WebRequestQueue.class) {
                if (webRequestQueue == null) {
                    webRequestQueue = new WebRequestQueue(context);
                }
            }
        }
        return webRequestQueue;
    }

    public void addToQueue(Request request, String tag) {
        request.setTag(tag);
        volleyRequestQueue.add(request);
    }

    public void cancelAll(String tag) {
        volleyRequestQueue.cancelAll(tag);
    }
}

MainActivity.java

    private final String REQUEST_TAG = getClass().getName();
    ...

    private void makeRequest() {
        ...
        WebRequestQueue.getInstance(this).addToQueue(stringRequest, REQUEST_TAG);
    }

    @Override
    protected void onStop() {
        super.onStop();
        WebRequestQueue.getInstance(this).cancelAll(REQUEST_TAG);
    }

Decoupling network code from activity using Eventbus

Even though the network code is much simpler now it is still coupled to the activity. With the help of EventBus, this decoupling can take place. EventBus is a library optimized for Android to make communication between components easier. Using EventBus is also simple. For classes that need to listen to events, they need to subscribe to the event bus and set callbacks for the events they want to listen to. Other classes can then post events to the event bus.

EventBus can be added to a project using gradle with the following:

compile 'de.greenrobot:eventbus:2.4.0'

GoogleWebRequest.java

public class GoogleStringRequest {
    private final EventBus eventBus;
    private Context context;
    private String tag;

    public GoogleStringRequest(Context context, String tag) {
        this.context = context;
        this.tag = tag;
        eventBus = EventBus.getDefault();
    }

    public void makeRequest() {
        String url = "http://www.google.com";
        StringRequest stringRequest = new StringRequest(
                Request.Method.GET,
                url,
                new Response.Listener<String>() {
                    @Override
                    public void onResponse(String response) {
                        eventBus.post(response);
                    }
                },
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        eventBus.post(error);
                    }
                });
        WebRequestQueue.getInstance(context).addToQueue(stringRequest, tag);
    }
}

MainActivity.java

public class MainActivity extends AppCompatActivity {
    private final String REQUEST_TAG = getClass().getName();
    private TextView textView;
    private EventBus eventBus;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(R.id.text);
        eventBus = EventBus.getDefault();
        eventBus.register(this);
        new GoogleStringRequest(this, REQUEST_TAG).makeRequest();
    }

    public void onEvent(String response) {
        textView.setText(response);
    }

    public void onEvent(VolleyError error) {
        textView.setText("That didn't work!");
    }

    @Override
    protected void onStop() {
        super.onStop();
        WebRequestQueue.getInstance(this).cancelAll(REQUEST_TAG);
        eventBus.unregister(this);
    }
}

Conclusion

With both Volley and EventBus networking code can be made simpler, cleaner, and decoupled from activities or fragments. The full example can be found on Github


Written by Jacob Oakes
I am a software architect who enjoys learning new things, clean code, and automated tests.