Android Core

Consume Webservice in Android using intentService

In this post, I will explain how to consume Restful Webservice using IntentService. We know we can consume a Webservice directly in the UI using for example Volley lib or a simple Http client, but there are some cases when we prefer to consume a Webservice in an Android Service.

We know that Android Service has a “longer” life than an Android Activity and if we want a task is completed even if the app UI is no longer available we should use a Service. We can consider for example the download scenario: let’s suppose we want to download some information from a remote server; we can do it in the UI (i.e Activity) but if the user, for example, moves to another app the download will be interrupted. If we want to be sure that the download continues even if the app isn’t anymore active we can use Service.

IntentService description

When we want to consume a Webservice, we have to be aware that the time required to contact the server and get the response can be quite long. Even if we execute this logic is a separate service, the service itself runs in the main thread, so that the calling activity can lag, waiting for the response from the service, and the OS can kill it showing the annoying ANR message. We can avoid this problem handling a separate thread inside the service, but Android SDK provides a specialized class, derived from the Service class, called IntentService defined as (from Android API docs):

“IntentService is a base class for Services that handle asynchronous requests (expressed as Intents) on demand. Clients send requests through startService(Intent) calls; the service is started as needed, handles each Intent in turn using a worker thread, and stops itself when it runs out of work.This “work queue processor” pattern is commonly used to offload tasks from an application’s main thread.”

Considering that the heavy work is made in a separate thread we don’t know when it will finish and the calling Activity is unaware of it. We will describe how to get back the information as the service retrieved data from the remote Webservice.

Creating the IntentService

As example we will use Yahoo! Finance Service to retrieve the current Ask and Bid value for a Stock. At the end we will build an app like this:
 
android_webservice_intentservice[4]

The first step is creating a service:

public class QuoteService extends IntentService {

    @Override
    protected void onHandleIntent(Intent intent) {
         // Here we do the heavy work
    }
}

At line 4, in onHandleIntent we do the “heavy work”, so we call the remote Webservice and parse the response. In this case we can simply use XmlPullParser to parse the data:

// Here we retrieve the stock quote using Yahoo! Finance
Log.d("Srv", "Get stock");
String url = YAHOO_FINANCE_URL.replaceAll("#sym#", stock.getSymbol());
try {
    HttpURLConnection con = (HttpURLConnection) ( new URL(url)).openConnection();
    con.setRequestMethod("GET");
    con.connect();
    InputStream is = con.getInputStream();

    // Start parsing XML
    XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
    parser.setInput(is, null);
    int event = parser.getEventType();
    String tagName = null;
    String currentTag = null;
    Stock stockResult = new Stock();

    while (event != XmlPullParser.END_DOCUMENT) {
        tagName = parser.getName();
        Log.d("Srv", "Tag:" + tagName);
        if (event == XmlPullParser.START_TAG) {
            currentTag = tagName;
        }
        else if (event == XmlPullParser.TEXT) {
            if ("ASK".equalsIgnoreCase(currentTag)) 
                //
            else if    ("BID".equalsIgnoreCase(currentTag)) {                        
                //
            }
        }

        event = parser.next();

    }

} catch (Exception e) {
    e.printStackTrace();
}

Then we define our service in the Manifest.xml:

<service android:name=".QuoteService" />

Activity: Webservice client

The next step is creating the client activity that invokes the service. We are using an IntentService then the calling Activity calls the service and then forget it. The activity is quite trivial, because as soon as the user clicks “Get quote!” button we invoke the service using:

startService(service_intent);

Anyway, there are two aspects we should consider:

  1. How we pass the data to the service
  2. How we get the data back from the service and show the information in UI

To pass data to the service we can create a simple java class that holds the stock symbol and the values we want to be filled by the service as we get the response. Our class should be a Parcelable class:

public class Stock implements Parcelable {

    private String symbol;
    private double askValue;
    private double bidValue;

    public Stock() {}

    public Stock(String symbol, double askValue, double bidValue) {
        this.symbol = symbol;
        this.askValue = askValue;
        this.bidValue = bidValue;
    }

    // get and set method

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        // We write the stock information in the parcel
        dest.writeString(symbol);
        dest.writeDouble(askValue);
        dest.writeDouble(bidValue);
    }

    public static final Creator<Stock> CREATOR = new Creator<Stock>() {

        @Override
        public Stock createFromParcel(Parcel source) {
            Stock stock = new Stock();
            stock.setSymbol(source.readString());
            stock.setAskValue(source.readDouble());
            stock.setBidValue(source.readDouble());
            return stock;
        }

        @Override
        public Stock[] newArray(int size) {
            return new Stock[0];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }
}

At line 18 and 25, we implement the two methods required to marshal and unmarshal our class.

Retrieve data from service using ResultReceiver

Another interesting aspect is how we can get the values back from the IntentService. As we, already, know the calling activity doesn’t wait for the result, so we have to find another way. Generally speaking there are different ways to solve this problem, in this post I will explain how to use ResultReceiver. This method is quite simple and elegant in my opinion.

The first step is creating a class that extends ResultReceiver, that we call QuoteServiceReceiver:

public class QuoteServiceReceiver extends ResultReceiver {

    private Listener listener;

    public QuoteServiceReceiver(Handler handler) {
        super(handler);        
    }

    public void setListener(Listener listener) {
        this.listener = listener;
    }

    @Override
    protected void onReceiveResult(int resultCode, Bundle resultData) {
        if (listener != null)
            listener.onReceiveResult(resultCode, resultData);
    }

    public static interface Listener {
        void onReceiveResult(int resultCode, Bundle resultData);
    }

}

This class is very simple. At line 19 we define an interface that the calling activity has to implement to get notified when the data isavailable. In this interface we define a Bundle that holds the data retrieved. Then at line 14 we override the onReceiveResult and call our callback method defined in the previous interface.

From the client point of view (so our Activity) we simply have to implements the interface defined and pass the QuoteServiceReceiver to the IntentService:

private Intent createCallingIntent(Stock stock) {
    Intent i = new Intent(this, QuoteService.class);
    QuoteServiceReceiver receiver = new QuoteServiceReceiver(new Handler());
    receiver.setListener(this);        
    i.putExtra("rec", receiver);
    i.putExtra("stock", stock);
    return i;
}

and when we call the service:

startService(createCallingIntent(stock));

..and in the callback method:

@Override
public void onReceiveResult(int resultCode, Bundle resultData) {
    Stock stock = resultData.getParcelable("stock");
    Log.d("Srv", "Stock ["+stock+"]");

    askValue.setText("" + stock.getAskValue());
    bidValue.setText("" + stock.getBidValue());
}

..and here we update the UI, with the data received.

In the service that consumes the remote Webservice:

@Override
protected void onHandleIntent(Intent intent) {

    Stock stock = intent.getParcelableExtra("stock");
    final ResultReceiver rec = (ResultReceiver) intent.getParcelableExtra("rec");
    ....
    // When data is ready
   if    ("BID".equalsIgnoreCase(currentTag)) {                        
          stockResult.setBidValue(Double.parseDouble(parser.getText()));
          Bundle b = new Bundle();
          b.putParcelable("stock", stockResult);
          rec.send(0, b);
    }
    ....
}
  • Source code available @ github

Francesco Azzola

He's a senior software engineer with more than 15 yrs old experience in JEE architecture. He's SCEA certified (Sun Certified Enterprise Architect), SCWCD, SCJP. He is an android enthusiast and he has worked for long time in the mobile development field.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
sandhya
sandhya
10 years ago

Hello,
I have gone through Bluetooth connection code by Katerina Zamani , and I am very much impressed with its precised explanation and now I am facing problem with its further part I.e connecting and communicating with other remote device..n I also refferd d “Bluetooth chat” by android SDK but I m unable to understand it..

So pleas u people post d rest part of Bluetooth activity, n I m waiting for ur reply….

thnku,

Back to top button