dotMobimobiForgemobiReadyDeviceAtlasgoMobi
background image

Consuming JSON services in Android apps

Image of duck consuming food from human hand
Posted by weimenglee - 19 Dec 2012
Twitter share icon Facebook share icon Google Plus share icon

Unless you are writing a Hello World Android application, chances are your application would need to connect to the outside world to fetch some data, such as live currency exchange rates, weather information, records from databases, etc. One of the easiest ways for your application to connect to the outside world is to use web services.

For the past few years, XML web services have dominated the arena for web services, as XML was touted as the ubiquitous medium for data exchange. However, using XML as the medium for your data payload suffers from the following problems:

1. XML representation is inherently heavy. The use of opening and closing tags add a lot of unnecessary weight to the payload. In the world of mobile applications, shaving a few bytes off the payload will dramatically improve the performance of applications, not to mention the reduction of data transferred over the expensive 3G and LTE wireless networks. This translates into cost savings for both application developers (who need to subscribe to expensive networks for their web servers) and users (who has limited amount of bandwidth to use per subscription).
2. XML representation is difficult to parse. While on the desktop, the DOM (Document Object Model) and SAX (Simple APIs for XML) are the two commonly used method for parsing XML Documents; on the mobile platform using DOM and SAX are very expensive, both computationally and in terms of memory requirements.

In recent years, another data interchange format has been gaining in popularity - JSON, or JavaScript Object Notation. Like XML, JSON is a text-based open standard for representing data, and it uses characters such as brackets "[{]}", colon ":" and comma ",", to represent data. Data are represented using simple key/value pairs, and more complex data are represented as associative arrays.

In this article, I will walk you through on how to consume a JSON service in your Android application.

Creating the Project

For this project, I will be using Eclipse with the Android 4.1 SDK. To start off, create an Android application project and name it as shown in Figure 1.


Figure 1: Create Android application project

In the res/layout folder, add in the following statements to the activity_main.xml file:

<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_alignParentLeft="true"
    android:layout_alignParentRight="true"
    android:layout_alignParentTop="true"
    android:orientation="vertical" >
 
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Latitude" />
 
    <EditText
        android:id="@+id/txtLat"
        android:layout_width="320dp"
        android:layout_height="wrap_content"
        android:ems="10"
        android:inputType="numberDecimal" 
        android:text="37.77493" />
 
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Longitude" />
 
    <EditText
        android:id="@+id/txtLong"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ems="10"
        android:inputType="numberDecimal"
        android:text="-122.419416"  />
 
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Get Weather" 
        android:onClick="btnGetWeather" />
 
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Postal Code" />
 
    <EditText
        android:id="@+id/txtPostalCode"
        android:layout_width="320dp"
        android:layout_height="wrap_content"
        android:ems="10"
        android:inputType="number"
        android:text="89118" />
 
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Get Places"
        android:onClick="btnGetPlaces" />
 
</LinearLayout>

This will create the UI as shown in Figure 2. As you can see, there are actually two parts to the UI:

1. The first part allows the user to enter a pair of latitude and longitude information. Click on the Get Weather button and you will be able to get information about the weather information for that particular location.
2. The second part allows the user to enter a postal code. Clicking the Get Places button will search for all the towns and cities in the world with this postal code.

In both cases, the data would be retrieved from web services hosted by GeoNames Web Services (http://www.geonames.org/export/web-services.html).


Figure 2: Create the UI

Creating the Helper Method

To connect to a web service, your application needs to first of all connect to the server using HTTP. You need to also determine if you will be using HTTP GET or HTTP POST. Once that is determined, you will fetch the data from the server and get ready for the next step, which is parsing. For this article, the GeoNames Web Services that you will be using uses HTTP GET, and hence, you will first of all create the helper method readJSONFeed() in the MainActivity.java file:

package net.learn2develop.json;
 
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
 
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
 
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
 
public class MainActivity extends Activity {
 
    public String readJSONFeed(String URL) {
        StringBuilder stringBuilder = new StringBuilder();
        HttpClient httpClient = new DefaultHttpClient();
        HttpGet httpGet = new HttpGet(URL);
        try {
            HttpResponse response = httpClient.execute(httpGet);
            StatusLine statusLine = response.getStatusLine();
            int statusCode = statusLine.getStatusCode();
            if (statusCode == 200) {
                HttpEntity entity = response.getEntity();
                InputStream inputStream = entity.getContent();
                BufferedReader reader = new BufferedReader(
                        new InputStreamReader(inputStream));
                String line;
                while ((line = reader.readLine()) != null) {
                    stringBuilder.append(line);
                }
                inputStream.close();
            } else {
                Log.d("JSON", "Failed to download file");
            }
        } catch (Exception e) {
            Log.d("readJSONFeed", e.getLocalizedMessage());
        }        
        return stringBuilder.toString();
    }
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
 
}

The readJSONFeed() method takes in a string representing the URL of the web service and then connects to the server using HTTP GET. You make use of the HttpClient class to connect to the server, the HttpGet class to specify the URL of the server, and the HttpResponse class to get the connection status from the server. Once the connection is established successfully, you all use the BufferedReader and InputStreamReader classes to download the result (which is a JSON string in this example) from the server. The readJSONFeed() method then returns the JSON string.

As you need Internet access for this project to work, remember to add the INTERNET permission in the AndroidManifest.xml file:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="net.learn2develop.json"
    android:versionCode="1"
    android:versionName="1.0" >
 
    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="15" />
    <uses-permission android:name="android.permission.INTERNET"/>
 
    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/title_activity_main" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
 
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
 
</manifest>

Getting Weather Information

Now that you can connect to the server to download the JSON result, it is now time to connect to the GeoNames Web Services to get weather information. In particular, to get the weather information of a particular location, you will use the following URL (replace the and with the actual latitude and longitude):

http://ws.geonames.org/findNearByWeatherJSON?lat=&lng=

A sample result from the server looks like this:

                {
                    "weatherObservation": {
                         "clouds":"scattered clouds",
                         "weatherCondition":"n/a",
                         "observation":"KCFV 090852Z AUTO 06005KT 
                          10SM SCT090 SCT110 24/20 A3000 RMK AO2 
                          SLP148 T02390200 53002",
                         "windDirection":60,
                         "ICAO":"KCFV",
                         "seaLevelPressure":1014.8,
                         "elevation":225,
                         "countryCode":"US",
                         "lng":-95.56666666666666,
                         "temperature":"23.9",
                         "dewPoint":"20",
                         "windSpeed":"05",
                         "humidity":78,
                         "stationName":"Coffeyville, Coffeyville 
                                     Municipal Airport",
                         "datetime":"2012-07-09 08:52:00",
                         "lat":37.083333333333336
                    }
                }

If you observe, you have a primary key - weatherObservation. Its value is a collection of key/value pairs, such as clouds, weatherCondition, etc.

In order to use the readJSONFeed() method, you need to call it asynchronously as beginning with Android 3.0 you can no longer call network operations from within your UI thread (such as within an activity). The easiest way to do this is to wrap it using an AsyncTask class. Hence, add the following statements to the MainActivity class:

package net.learn2develop.json;
 
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
 
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONObject;
 
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
 
public class MainActivity extends Activity {
 
    public String readJSONFeed(String URL) {
        StringBuilder stringBuilder = new StringBuilder();
        HttpClient httpClient = new DefaultHttpClient();
        HttpGet httpGet = new HttpGet(URL);
        try {
            HttpResponse response = httpClient.execute(httpGet);
            StatusLine statusLine = response.getStatusLine();
            int statusCode = statusLine.getStatusCode();
            if (statusCode == 200) {
                HttpEntity entity = response.getEntity();
                InputStream inputStream = entity.getContent();
                BufferedReader reader = new BufferedReader(
                        new InputStreamReader(inputStream));
                String line;
                while ((line = reader.readLine()) != null) {
                    stringBuilder.append(line);
                }
                inputStream.close();
            } else {
                Log.d("JSON", "Failed to download file");
            }
        } catch (Exception e) {
            Log.d("readJSONFeed", e.getLocalizedMessage());
        }        
        return stringBuilder.toString();
    }
 
    private class ReadWeatherJSONFeedTask extends AsyncTask
    <String, Void, String> {
        protected String doInBackground(String... urls) {
            return readJSONFeed(urls[0]);
        }
 
        protected void onPostExecute(String result) {
            try {
                JSONObject jsonObject = new JSONObject(result);
                JSONObject weatherObservationItems = 
                    new JSONObject(jsonObject.getString("weatherObservation"));
 
                Toast.makeText(getBaseContext(), 
                    weatherObservationItems.getString("clouds") + 
                 " - " + weatherObservationItems.getString("stationName"), 
                 Toast.LENGTH_SHORT).show();
            } catch (Exception e) {
                Log.d("ReadWeatherJSONFeedTask", e.getLocalizedMessage());
            }          
        }
    }
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

In the ReadWeatherJSONFeedTask class, you have two methods:

1. The doInBackGround() method is executed asynchronously. Here, you call the readJSONFeed() method to get the weather information. When the result is obtained, it is returned to the onPostExecute() method.
2. The onPostExecute() method takes the JSON result and parses it.
a. First, the JSON string is passed as the argument to the constructor of the JSONObject class. This creates a new JSONObject object (jsonObject) with key/value mappings from the JSON string.
b. You then get the value of the weatherObservation key by using the getString() method of jsonObject. The values are then passed as the constructor of the JSONObject class, creating another JSONObject - weatherObservationItems.
c. Finally, you extract the value of the clouds and stationName keys by calling the getString() method of weatherObservationItems.

To wire up the event handler for the Get Weather button in your UI, add the btnGetWeather() method to MainActivity.java:

package net.learn2develop.json;
 
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
 
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONObject;
 
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
 
public class MainActivity extends Activity {
 
    public String readJSONFeed(String URL) {
       ...
    }
 
    private class ReadWeatherJSONFeedTask extends AsyncTask
    <String, Void, String> {
       ...
    }
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
 
    public void btnGetWeather(View view) {
        EditText txtLat = (EditText) findViewById(R.id.txtLat);
        EditText txtLong = (EditText) findViewById(R.id.txtLong);
 
        new ReadWeatherJSONFeedTask().execute(
            "http://ws.geonames.org/findNearByWeatherJSON?lat=" + 
            txtLat.getEditableText().toString() + "&lng=" + 
            txtLong.getText().toString());        
    }
 
}

You can now test the application on an Android emulator. Figure 3 shows the result.


Figure 3: Test in Emulator

Getting Places using Postal Code

Now that you have managed to consume the first JSON service, let's take a look at the second example. This time, you will consume a service that returns a list of city names using a given postal code. You can get the result from the following URL (replace the with the actual postal code):

http://api.geonames.org/postalCodeSearchJSON?postalcode=&maxRows=10&username=demo

A sample result from the server looks like this:

                {
                    "postalCodes": [
                        { 
                            "adminCode3":"3203",
                            "adminName2":"Wahlkreis St. Gallen",
                            "adminName3":"St. Gallen",
                            "adminCode2":"1721",
                            "adminCode1":"SG",
                            "postalCode":"9011",
                            "countryCode":"CH",
                            "lng":9.399858534040646,
                            "placeName":"St. Gallen",
                            "lat":47.414775328611945,
                            "adminName1":"Kanton St. Gallen"
                        },
                        {
                            "adminCode1":"GS",
                            "postalCode":"9011",
                            "countryCode":"HU",
                            "lng":17.781944437499998,
                            "placeName":"Gyor",
                            "lat":47.607638900000005,
                            "adminName1":"Gyor-Moson-Sopron"
                        },
                        {
                            "adminName2":"Tromsø",
                            "adminCode2":"1902",
                            "adminCode1":"19",
                            "postalCode":"9011",
                            "countryCode":"NO",
                            "lng":18.95508,
                            "placeName":"Tromsø",
                            "lat":69.6489,
                            "adminName1":"Troms"
                        }, 
                        {
                            ...
                            ...
                        }
                    ]
                }

If you observe, you have a primary key - postalCodes. Its value is an array of objects, with each object containing a collection of key/value pairs, such as adminCode1, lat, lng, etc.

Like the previous example, you will add a ReadPlacesFeedTask class to connect to the server asynchronously:

package net.learn2develop.json;
 
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
 
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONObject;
 
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
 
public class MainActivity extends Activity {
 
    public String readJSONFeed(String URL) {
        ...
    }
 
    private class ReadWeatherJSONFeedTask extends AsyncTask
    <String, Void, String> {
        ... 
    }
 
    private class ReadPlacesFeedTask extends AsyncTask
    <String, Void, String> {
        protected String doInBackground(String... urls) {
            return readJSONFeed(urls[0]);
        }
 
        protected void onPostExecute(String result) {
            try {
                JSONObject jsonObject = new JSONObject(result);
                JSONArray postalCodesItems = new 
                    JSONArray(jsonObject.getString("postalCodes"));
 
                //---print out the content of the json feed---
                for (int i = 0; i < postalCodesItems.length(); i++) {
                    JSONObject postalCodesItem = 
                        postalCodesItems.getJSONObject(i);        
                    Toast.makeText(getBaseContext(), 
                           postalCodesItem.getString("postalCode") + " - " + 
                        postalCodesItem.getString("placeName") + ", " +
                        postalCodesItem.getString("countryCode"), 
                        Toast.LENGTH_SHORT).show();                                    
                }
            } catch (Exception e) {
                Log.d("ReadPlacesFeedTask", e.getLocalizedMessage());
            }          
        }
    }
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
 
    public void btnGetWeather(View view) {
        ...
    }
}

As in the previous example, the result is first converted into a JSONObject. The values of the postalCode key is then converted into a JSONArray object - postalCodesItems, which contains an array of JSONObject objects. You then iterate through the array and extracted each object and print out the values of the postalCode, placeName, and countryCode keys.

Finally, wire the event handler for the Get Places button with the btnGetPlaces() method in the MainActivity.java file:

    public void btnGetWeather(View view) {
        ...
    }
 
    public void btnGetPlaces(View view) {
        EditText txtPostalCode = (EditText) findViewById(R.id.txtPostalCode);
        new ReadPlacesFeedTask().execute(        
            "http://api.geonames.org/postalCodeSearchJSON?postalcode=" + 
            txtPostalCode.getEditableText().toString() + 
            "&maxRows=10&username=demo");            
    }

Figure 4 shows the sample result.


Figure 4: Voilà - the result

Summary

In this article, you have seen how to consume a JSON service from within your Android application. You have seen two examples on how to parse a JSON string using the JSONObject and JSONArray classes available in the org.json package.


Posted by weimenglee - 19 Dec 2012

weimenglee's picture

Wei-Meng Lee is a technologist and the founder of Developer Learning Solutions (http://www.learn2develop.net), a company focusing on hands-on training on the latest technology. Wei-Meng specializes in mobile technologies and has written several books on .NET, VB, C#, and .NET Compact Framework and is currently working on an Android book for Wrox. Contact Wei-Meng Lee at .

Posted by student 1 year ago

Dear Sir!

This tutorial is wunderful Helped me a lot. I am trying to use the same for a Student Project. I am doing a Login an a user, and app has to connected to a webservice via JSON. I get a JSON format from the webservice.
e.g. I created a class Person an within webservice has to check for a username and password.
Queston shoudl I use Person person = gson.fromJSON( JSON format) or how. Because afterthat I have to check the returned JSON format with the given if is correct.

I would be very glad to hear from you soon,

regads

Posted by gowthamj2008 1 year ago

Dear Sir,
Your Beginning Android Development is my First Android Book.

This tutorial is Excellent. Thanks.

Posted by amanoel 1 year ago

Dear Sir,

This tutorial was excellent for me. Thanks, very much.
I executed this project, but i didn´t get good results pressing the Get Places button, with the message:
{"status":{"message":"the deaily limit of 30000 credits for demo has been exceeded. Please use an application specific account. Do not use the demo account for your application.","value":18}}
Please, let me know how to get a valid user for use the geonames.

Thanks in advance.

Antonio Manoel

Posted by DietElixirs 1 year ago

I haven't got a conversion to JSONObject. I tried everything, don't know what's the problem.

Posted by ashishpatil396 1 year ago

this is very best site for developer...

Posted by Su Mon Zaw 1 year ago

Dear Sir,

I want to know that it is same or not when I use my own web service server application not from Internet .Can I use same as your code in android to parse JSON from my server side?Thanks for your information.It is excellent.

Posted by garymoody2014 23 weeks ago

Love the coverage out there showing coding movements across multiple verticals.

Professional Web Development services company - http://www.siliconithub.com