Search code examples
androidasynchronousinterfacesolid-principlessingle-responsibility-principle

Android - Separate async http code from the activity according to SOLID principles


I want to separate async http code from the activities as I'm reusing the code. This is what I am doing currently:

I want to get a list of projects form a REST API and store it in an array. (Assume I'm not using local caching as I want to load them every time)

ProjectInterface.java

public interface ProjectInterface {
    public void onProjectsLoaded(ArrayList<Project> projectArrayList);
}

HttpProjectList.java

//async class will get the results from the server and send to the activity 
public class HttpProjectList {

  protected ProjectInterface interface;
  protected Context c;

  public HttpProjectList(ProjectInterface interface, Context c){
    this.interface = interface;
    this.c = c;
  }

  //Run Async task Volley here
  public void projectListRequest() {    
    RequestQueue requestQueue = Volley.newRequestQueue(this.c);
    JsonArrayRequest req = new JsonArrayRequest(Path.WORK_ORDERS, success, error);
    requestQueue.add(req);  
  }

 //on success set the data
 private Response.Listener<JSONArray> success = new Response.Listener<JSONArray>() {
    @Override
    public void onResponse(JSONArray response) {
      //do dome parsing here and populate the array
      interface.onProjectsLoaded(projectArrayList);
    }
 }

 //error function here, i'm not typing

}

MyActivity.java

//I'm calling all in this activity
public class BaseActivity extends Activity implements ProjectInterface {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(setView());

    HttpProjectList httpList = new HttpProjectList(this, getBaseContext());
    httpList.projectListRequest(); //fire http call
  }

  //run after projects loaded
  @Override
  public void onProjectsLoaded(ArrayList<Project> projectArrayList) {
     //Do something with the server data
  }

}

In this case I have one API that populate the array. What if I have multiple api calls? Like

  1. User list
  2. Contractor list
  3. Task list
  4. POST a new task to the server etc

How can I organise code according to OOP and SOLID principles? These are the techniques I have In my mind, but I don't know which one is the best.

  1. Create separate classes for all the Async functions. According to the above example I have 1 interface and 1 http service handler class. In the same way, I will create 2 sets of classes for every api call

  2. Create one interface call HttpInterface and put all the call back functions in it, and create another HttpHandler class and put all the http methods of all the API calls

Which one is better? Or is there any other way? Can you give some advice on this? Thanks a lot!


Solution

  • Both your suggested approaches follow the Observer´s pattern. I would personally suggest the second approach where all the methods are encapsulated in one single interface. However, if the number of methods rises (lets say above 5) then maybe you need to consider to group them in a way that may reflect the functionality of an object. For example you might have:

    public interface HttpUserListener {
    
        public void onUserListReceived(List<User> users);
        public void onUserAdd(User user);
        public void onUserRemove(User user);
    }
    
    
    public interface HttpProjectListener {
    
        public void onProjectListReceived(List<Project> projects);
        public void onProjectAssigned(Project project, User user);
        public void onProjectComplete(Project project);
    }
    

    On the implementation part probably you need a List of listeners to be called on particular event:

    public class HttpHandler {
    
      protected List<HttpUserListener> userListeners;
      protected List<HttpProjectListener> projectListeners;
      protected Context c;
    
      public HttpHandler(Context c){
        this.c = c;
      }
    
      public void registerListener(UserListener listener){
    
         if(userListeners == null){
    
            userListeners = new ArrayList<UserListener>(1);
         }
         userListener.add(listener);
      }
    
      public void unregisterListener(UserListener listener){
    
         if(userListeners == null){
           return;
         }
         userListener.remove(listener);
      }
    
      public void asyncAddUser(User user){
    
        // provide the async method and call the listeners
        if(userListeners != null)
          for(UserListener l: userListeners)
             l.onUserAdd(user);
      }
    
    }
    

    I want to emphasize that the ideal place of creating and handling async tasks in Android are Services. so the HttpHandler would extend a Service (or IntentService which already provides a worker thread) and you can register and unregister listeners to this service by using binding approach. This is a good tutorial from Android developers website.

    Alternatively you can forget about the traditional listener´s approach and have a single service which will broadcast intents when an HTTP call takes place. And have your activities capture these broadcasts and react accordingly. This is closer to Android´s programming architecture.