lundi 21 octobre 2013

Android: Re-using AsyncTask class across multiple Activities.

First I will like to start by apologizing for not putting this up a long time ago after several email request. I have very little time to put these Tutorials together for public consumption and I prefer responding with short snippets via email. Anyway.. that aside.

This post assumes you have a basic understanding of Android Development precisely about Activities and AsyncTasks. You can visit respective links to learn more on the official android documentation.

I will try to explain how to use a single Async class across Activities that make asynchronous request to a web service.

Suppose you have a REST API that always responds with some JSON output (which we will get as a string) and you need to call this API in multiple (greater than two) Activities. It will be good to have a common AsyncTask class, where you only pass an entry-point URL and a set of parameters and it returns the response. If you however have only a single activity that does some asynchronous task, it will be good design to implement it's AsyncTask class as a private sub class. So, how can we go about implementing a single AsyncTask class to be used in multiple Activities?


  1. We will implement a sub class of the AsyncTask class which will define an interface that MUST be implemented in the calling Activity. This subclass will take as parameters the calling activity (type: Activity), method (POST or GET type: String) and a set of parameters (type:  List<namevaluepair>). Three constructors will be implemented to make the last two parameters optional.
  2. We will show a ProgressDialog while the request is being executed in the background.
  3. We demonstrate the use of this class by defining and example Activity.

A. The subclass: AsyncRequest.java

package com.sewoyebah.examples;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URLEncoder;
import java.util.List;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.AsyncTask;

public class AsyncRequest extends AsyncTask<String, Integer, String> {

 OnAsyncRequestComplete caller;
 Context context;
 String method = "GET";
 List<NameValuePair> parameters = null;
 ProgressDialog pDialog = null;

 // Three Constructors
 public AsyncRequest(Activity a, String m, List<NameValuePair> p) {
  caller = (OnAsyncRequestComplete) a;
  context = a;
  method = m;
  parameters = p;
 }

 public AsyncRequest(Activity a, String m) {
  caller = (OnAsyncRequestComplete) a;
  context = a;
  method = m;
 }

 public AsyncRequest(Activity a) {
  caller = (OnAsyncRequestComplete) a;
  context = a;
 }

 // Interface to be implemented by calling activity
 public interface OnAsyncRequestComplete {
  public void asyncResponse(String response);
 }

 public String doInBackground(String... urls) {
  // get url pointing to entry point of API
  String address = urls[0].toString();
  if (method == "POST") {
   return post(address);
  }

  if (method == "GET") {
   return get(address);
  }

  return null;
 }

 public void onPreExecute() {
  pDialog = new ProgressDialog(context);
  pDialog.setMessage("Loading data.."); // typically you will define such
            // strings in a remote file.
  pDialog.show();
 }

 public void onProgressUpdate(Integer... progress) {
  // you can implement some progressBar and update it in this record
  // setProgressPercent(progress[0]);
 }

 public void onPostExecute(String response) {
  if (pDialog != null && pDialog.isShowing()) {
   pDialog.dismiss();
  }
  caller.asyncResponse(response);
 }

 protected void onCancelled(String response) {
  if (pDialog != null && pDialog.isShowing()) {
   pDialog.dismiss();
  }
  caller.asyncResponse(response);
 }

 @SuppressWarnings("deprecation")
 private String get(String address) {
  try {

   if (parameters != null) {

    String query = "";
    String EQ = "="; String AMP = "&";
    for (NameValuePair param : parameters) {
     query += param.getName() + EQ + URLEncoder.encode(param.getValue()) + AMP;
    }

    if (query != "") {
     address += "?" + query;
    }
   }

   HttpClient client = new DefaultHttpClient();
   HttpGet get= new HttpGet(address);

   HttpResponse response = client.execute(get);
   return stringifyResponse(response);

  } catch (ClientProtocolException e) {
   // TODO Auto-generated catch block
  } catch (IOException e) {
   // TODO Auto-generated catch block
  }

  return null;
 }

 private String post(String address) {
  try {

   HttpClient client = new DefaultHttpClient();
   HttpPost post = new HttpPost(address);

   if (parameters != null) {
    post.setEntity(new UrlEncodedFormEntity(parameters));
   }

   HttpResponse response = client.execute(post);
   return stringifyResponse(response);

  } catch (ClientProtocolException e) {
   // TODO Auto-generated catch block
  } catch (IOException e) {
   // TODO Auto-generated catch block
  }

  return null;
 }

 private String stringifyResponse(HttpResponse response) {
  BufferedReader in;
  try {
   in = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));

   StringBuffer sb = new StringBuffer("");
   String line = "";
   while ((line = in.readLine()) != null) {
    sb.append(line);
   }
   in.close();

   return sb.toString();
  } catch (IllegalStateException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (IOException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }

  return null;
 }
}

First we define the three constructors that our class should support, the first supports three parameters, the second two and the third one. Next we define the interface which the calling
Activity must implement and hence override the asyncResponse() method. Next we implement the required inherited methods. Depending on the method specified, we call the get() or post() private functions
which will return the server response that will be passed to the onPostExecute() method and back to our Activity via the implemented interface.

Please read the official Documentation on AsyncTask to have an understanding of the inherited methods. In summary onPreExecute() is called just before the request is sent, doInBackgroud() is what actually does the job and returns a value which a passed to onPostExecute() called after request is completed. If request was cancelled, onCancelled() is called instead of onPostExecute(). A typical example of a canceled request is when user rotates a device when request is not completed. If you didn't handle the activity life cycle properly then Activity will be redrawn and another request will be made (cancelling the previous).

We also use here a progressDialog to indicate to the user something  is being executed. It is initialized in the onPreExecute() method because we don't want to do this in each constructor of the class and besides we still have hold of the UI thread in the onPreExecute() method.

B. The Example Activity: PostsActivity.java

package com.sewoyebah.examples;

import java.util.ArrayList;

import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.os.Bundle;
import android.app.Activity;
import android.widget.TextView;

public class PostsActivity extends Activity implements
  AsyncRequest.OnAsyncRequestComplete {

 TextView titlesView;
 String apiURL = "http://www.example.com/apis/posts";
 ArrayList<NameValuePair> params;

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

  // TODO Auto-generated method stub
  titlesView = (TextView) findViewById(R.id.post_titles);
  params = getParams();
  AsyncRequest getPosts = new AsyncRequest(this, "GET", params);
  getPosts.execute(apiURL);
 }

 // override method from AsyncRequest.OnAsyncRequestComplete interface
 // the response can contain other parameters specifying if the request was
 // successful or not and other things
 // in a real world application various checks will be done on the response
 @Override
 public void asyncResponse(String response) {

  try {
   // create a JSON array from the response string
   JSONArray objects = new JSONArray(response);
   // define a string to hold out titles (in a real word application you will be using a ListView and an Adapter to do such listing)
   String titles = "";
   String NL = "\n";
   String DOT = ". ";
   for (int i = 0; i < objects.length(); i++) {
    JSONObject object = (JSONObject) objects.getJSONObject(i);
    titles += object.getString("id") + DOT + object.getString("title") + NL;
   }
   titlesView.setText(titles);
  } catch (JSONException e) {
   e.printStackTrace();
  }
 }

 // here you specify and return a list of parameter/value pairs supported by
 // the API
 private ArrayList<NameValuePair> getParams() {
  // define and ArrayList whose elements are of type NameValuePair
  ArrayList<NameValuePair> params = new ArrayList<NameValuePair>();
  params.add(new BasicNameValuePair("start", "0"));
  params.add(new BasicNameValuePair("limit", "10"));
  params.add(new BasicNameValuePair("fields", "id,title"));
  return params;
 }

}

This is a pretty straight forward Android Activity. In this example we will assume our response is a JSON array. That is, an array of JSON Objects. For example:

 [{"id": 1, "title": "Title of post 1"}, {"id": 2, "title": "Title of post 2"}, {"id": 3, "title": "Title of post 3"}];

Observe that the class implements the AsyncRequest.OnAsyncRequestComplete interface of the AsyncRequest and hence must override the asyncResponse() method of this interface.

To execute the request in the onCreate() method, we define the set of parameters using the getParams() method, create an instance of the AsyncRequest class and call it's execute() method passing to it the entry point (URL) of your API. It is common to use and ArrayList of NameValuePairs as a paramter set.

The response will be received and parsed as a String by our AsyncRequest class and passed to the asyncResponse() interface method as a string.

In the asyncResponse(), we create a JSONArray from this string and read each object and display a list of titles. In a typical real world application, you will implement this using a ListView and an Adapter but this is out of the scope of this tutorial. Here we just concatenate the titles as a String and display in a TextView.


C. Another Example Activity: PostActivity.java

Another example of an activity could be one that gets the details of a post using the post ID. This will typically be launched as an Intent from the Listing Activity. In my next post I will try to explain the same concept using a ListView and an ArrayAdapter to display a list of posts and onClick opens the details of a post in another activity

package com.sewoyebah.examples;

import java.util.ArrayList;

import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
import org.json.JSONException;
import org.json.JSONObject;

import android.os.Bundle;
import android.app.Activity;
import android.widget.TextView;

public class PostActivity extends Activity implements
  AsyncRequest.OnAsyncRequestComplete {

 TextView postView;
 String apiURL = "http://www.example.com/apis/posts";
 ArrayList<NameValuePair> params;
 String postID = "75";

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

  // TODO Auto-generated method stub
  postView = (TextView) findViewById(R.id.post_view);
  params = getParams();
  AsyncRequest getPosts = new AsyncRequest(this, "GET", params);
  getPosts.execute(apiURL);
 }

 // override method from AsyncRequest.OnAsyncRequestComplete interface
 @Override
 public void asyncResponse(String response) {

  try {
   // create a JSON array from the response string
   JSONObject postObject = new JSONObject(response);
   // define a string to hold out titles
   // (in a real word application you will be using a ListView and an
   // Adapter to do such listing)
   String post = "";
   String NL = "\n";
   post += postObject.getString("title") + NL + NL;
   post += postObject.getString("description");
   postView.setText(post);
  } catch (JSONException e) {
   e.printStackTrace();
  }
 }

 // here you specify and return a list of parameter/value pairs supported by
 // the API
 private ArrayList<NameValuePair> getParams() {
  // define and ArrayList whose elements are of type NameValuePair
  ArrayList<NameValuePair> params = new ArrayList<NameValuePair>();
  params.add(new BasicNameValuePair("id", postID));
  return params;
 }

}
In case you want to fully test code, bellow are the Layout xml files:

/res/layout/activity_posts.xml
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".PostsActivity" >

    <TextView
        android:id="@+id/post_titles"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</RelativeLayout>

/res/layout/activity_post.xml
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".PostActivity" >

    <TextView
        android:id="@+id/post_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</RelativeLayout>
So in summary after defining the AsyncRequest class this is what you do for each Activity that is to use it:

Class definition
public class MyActivity extends Activity implements AsyncRequest.OnAsyncRequestComplete

Define set of paramters:

ArrayList<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("id", "75"));
params.add(new BasicNameValuePair("attributes", "title,description"));

Start our async task

AsyncRequest request = new AsyncRequest(this, 'GET', params);
request.execute(apiURL);

And implement our asyncResponse() method which will be called after the async request is completed

public void asyncResponse(String response) { ... }

Sending multiple asynchronous requests in same activity

One will now ask: Is it possible to send multiple asynchronous requests from same activity using the AsyncRequest class? The answer is yes but you need to add a bit more to the above solution and keep in mind there is a limit to the number of threads you can run simultaneously based on the API version your app supports. See Android documentation for details.

First you assign a label to each request and pass that label to the AsyncRequest Class and back to the asyncResponse()  method. Your constructor could be like

public AsyncRequest(Activity a, String m, List<NameValuePair> p, String l) {
  caller = (OnAsyncRequestComplete) a;
  context = a;
  method = m;
  parameters = p;
  label = l;
 }

And your interface defined as

public interface OnAsyncRequestComplete {
 public void asyncResponse(String response, String label);
}

and when calling the interface after the request is complete in the postExecute() or isCancelled()  methods you do

caller.asyncResponse(response, label);

and finally back in the Activity you implement your asyncResponse() method as follows

public void asyncResponse(String response, String label) {

 swtich(label) {
  case "get_posts_request"
  // call some method to complete the request
  // you initialized your class with new AsyncRequest(this, "GET", params, "get_posts_request")
  completeGetPostRequest(response);
  break;
  
  case "some_other_label"
  // call some method to complete the request
  // you initialized your class with new AsyncRequest(this, "POST", params, "some_other_label")
  completeSomeOtherRequest(response);
  break;
 }
   
}


Hope this gives some insights..

46 commentaires:

  1. Hi this code was working in fine in activity but not working in Fragment

    RépondreSupprimer
  2. maybe this is too late..
    But.. to make this work in Fragments you can add another custom Constructor:

    public AsyncRequest(OnAsyncRequestComplete c, Activity a, String m, List p, String l) {
    caller = c;
    context = a;
    method = m;
    parameters = p;
    label = l;
    }

    and call it like this:
    AsyncRequest getPosts = new AsyncRequest(TheFragment.this,getActivity(), "GET", params);

    RépondreSupprimer

  3. It's interesting that many of the bloggers your tips helped to clarify a few things for me as well as giving... very specific nice content.Android Training institute in chennai with placement | Android Training in chennai

    RépondreSupprimer
  4. This information is impressive; I am inspired with your post writing style & how continuously you describe this topic. After reading your post, thanks for taking the time to discuss this, I feel happy about it and I love learning more about this topic.Android Training|Android Training in chennai with placement | Android Training in velachery

    RépondreSupprimer
  5. "Great blog created by you. I read your blog, its best and useful information. You have done a great work. Super blogging and keep it up.php jobs in hyderabad.
    "

    RépondreSupprimer
  6. "Great blog created by you. I read your blog, its best and useful information. You have done a great work. Super blogging and keep it up.php jobs in hyderabad.
    "

    RépondreSupprimer
  7. "Great blog created by you. I read your blog, its best and useful information. You have done a great work. Super blogging and keep it up.php jobs in hyderabad.
    "

    RépondreSupprimer
  8. Hi this code was working in fine in activity but not working in Fragmen.
    AWS Jobs in Hyderabad

    RépondreSupprimer
  9. This is very informative blog for learners, Thanks for sharing this information Android Online Training Hyderabad

    RépondreSupprimer
  10. • Nice and good article. It is very useful for me to learn and understand easily. Thanks for sharing your valuable information and time. Please keep updatingAzure Online Training HYDERABAD

    RépondreSupprimer
  11. • Nice and good article. It is very useful for me to learn and understand easily. Thanks for sharing your valuable information and time. Please keep updatingAzure Online course

    RépondreSupprimer
  12. • Nice and good article. It is very useful for me to learn and understand easily. Thanks for sharing your valuable information and time. Please keep updating. Power Bi Online Training

    RépondreSupprimer
  13. Thanks a lot very much for the high quality and results-oriented help. I won’t think twice to endorse your blog post to anybody who wants and needs support about this area.
    digital marketing training in annanagar

    digital marketing training in marathahalli

    digital marketing training in rajajinagar

    Digital Marketing online training

    full stack developer training in pune

    RépondreSupprimer
  14. The knowledge of technology you have been sharing thorough this post is very much helpful to develop new idea. here by i also want to share this.
    full stack developer training in annanagar

    full stack developer training in tambaram

    full stack developer training in velachery

    RépondreSupprimer
  15. Really you have done great job,There are may person searching about that now they will find enough resources by your post
    python training institute in chennai
    python training in Bangalore
    python training institute in chennai

    RépondreSupprimer
  16. That was a great message in my carrier, and It's wonderful commands like mind relaxes with understand words of knowledge by information's.
    Blueprism training in Chennai

    Blueprism training in Bangalore

    Blueprism training in Pune

    RépondreSupprimer
  17. This is a nice post in an interesting line of content.Thanks for sharing this article, great way of bring this topic to discussion.

    Data Science training in Chennai
    Data science training in bangalore
    Data science online training
    Data science training in pune

    RépondreSupprimer
  18. A very nice guide. I will definitely follow these tips. Thank you for sharing such detailed article. I am learning a lot from you.

    java training in chennai | java training in bangalore

    java online training | java training in pune

    RépondreSupprimer
  19. Excellant post!!!. The strategy you have posted on this technology helped me to get into the next level and had lot of information in it.

    angularjs Training in chennai
    angularjs-Training in pune

    angularjs-Training in chennai

    angularjs Training in chennai

    angularjs-Training in tambaram

    RépondreSupprimer
  20. Whoa! I’m enjoying the template/theme of this website. It’s simple, yet effective. A lot of times it’s very hard to get that “perfect balance” between superb usability and visual appeal. I must say you’ve done a very good job with this.


    AWS Training in Bangalore | Amazon Web Services Training in bangalore , india

    AWS Training in pune | Amazon Web Services Training in Pune, india

    AWS Training in Chennai|Amazon Web Services Training in Chennai,India



    aws online training and certification | amazon web services online training ,india


    RépondreSupprimer
  21. Amazing Article ! I have bookmarked this article page as i received good information from this. All the best for the upcoming articles. I will be waiting for your new articles. Thank You ! Kindly Visit Us @ Coimbatore Travels | Ooty Travels | Coimbatore Airport Taxi

    RépondreSupprimer
  22. Really you have done great job,There are may person searching about that now they will find enough resources by your post
    Devops Training courses
    Devops Training in Bangalore
    Best Devops Training in pune
    Devops interview questions and answers

    RépondreSupprimer
  23. Good Post! Thank you so much for sharing this pretty post, it was so good to read and useful to improve my knowledge as updated one, keep blogging.
    Best Devops training in sholinganallur
    Devops training in velachery
    Devops training in annanagar
    Devops training in tambaram

    RépondreSupprimer
  24. I found your blog while searching for the updates, I am happy to be here. Very useful content and also easily understandable providing.. Believe me I did wrote an post about tutorials for beginners with reference of your blog. 
    Java training in Bangalore | Java training in Btm layout

    Java training in Bangalore | Java training in Jaya nagar

    Java training in Bangalore | Java training in Electronic city

    Java training in Chennai | Java training institute in Chennai | Java course in Chennai

    RépondreSupprimer
  25. She noticed a wide variety of pieces, with the inclusion of what it is like to have an awesome helping style to have the rest without hassle grasp some grueling matters.
    iosh course in chennai

    RépondreSupprimer
  26. This is most informative and also this post most user friendly and super navigation to all posts... Thank you so much for giving this information to me.. 
    python Online training in chennai
    python Online training in bangalore
    python interview question and answers

    RépondreSupprimer
  27. Hello I am so delighted I found your blog, I really found you by mistake, while I was looking on Yahoo for something else, anyways I am here now and would just like to say thanks for a tremendous post. Please do keep up the great work.
    Java training in Chennai | Java training institute in Chennai | Java course in Chennai

    Java training in Bangalore | Java training institute in Bangalore | Java course in Bangalore

    Java online training | Java Certification Online course-Gangboard

    Java training in Pune

    RépondreSupprimer
  28. This is an awesome post.Really very informative and creative contents. These concept is a good way to enhance the knowledge.I like it and help me to development very well.Thank you for this brief explanation and very nice information.Well, got a good knowledge.
    python interview questions and answers
    python tutorials
    python course institute in electronic city

    RépondreSupprimer
  29. Have you been thinking about the power sources and the tiles whom use blocks I wanted to thank you for this great read!! I definitely enjoyed every little bit of it and I have you bookmarked to check out the new stuff you post
    python interview questions and answers
    python tutorials
    python course institute in electronic city

    RépondreSupprimer
  30. Your good knowledge and kindness in playing with all the pieces were very useful. I don’t know what I would have done if I had not encountered such a step like this.
    Selenium Online training | Selenium Certification Online course-Gangboard

    Selenium interview questions and answers

    Selenium interview questions and answers

    RépondreSupprimer
  31. Thank you for taking the time and sharing this information with us. It was indeed very helpful and insightful while being straight forward and to the point.
    Best Devops training in sholinganallur
    Devops training in velachery
    Devops training in annanagar
    Devops training in tambaram

    RépondreSupprimer
  32. This is an awesome post.Really very informative and creative contents. These concept is a good way to enhance the knowledge.I like it and help me to development very well.Thank you for this brief explanation and very nice information.Well, got a good knowledge.

    rpa training in bangalore | best rpa training in bangalore | rpa course in bangalore | rpa training institute in bangalore | rpa training in bangalore | rpa online training | rpa training in Chennai

    RépondreSupprimer
  33. Hello! This is my first visit to your blog! We are a team of volunteers and starting a new initiative in a community in the same niche. Your blog provided us useful information to work on. You have done an outstanding job.
    AWS Training in Marathahalli | Best AWS Training in Bangalore
    Amazon Web Services Training in Chennai | No.1 AWS Training for Solution Architect in Chennai

    RépondreSupprimer
  34. Wow it is really wonderful and awesome thus it is very much useful for me to understand many concepts and helped me a lot. it is really explainable very well and i got more information from your blog.

    best rpa training in chennai
    rpa training in chennai
    rpa interview questions and answers
    automation anywhere interview questions and answers
    blueprism interview questions and answers
    uipath interview questions and answers
    rpa training in bangalore

    RépondreSupprimer
  35. Wow it is really wonderful and awesome thus it is very much useful for me to understand many concepts and helped me a lot. it is really explainable very well and i got more information from your blog.

    best rpa training in chennai
    rpa training in chennai
    rpa interview questions and answers
    automation anywhere interview questions and answers
    blueprism interview questions and answers
    uipath interview questions and answers
    rpa training in bangalore

    RépondreSupprimer