If the request to the Web service is a simple JSON object, then you can convert the HTTP response to a String object and from there convert it to a Java Object using the Google JSON parser. But sometimes you might come across a huge data set that is being sent from the back-end server and you have to parse that information to either display it or save it to the Sqlite database for later use resulting in Java out of memory heap issues if you take the first approach.
Well in a situation like that the best solution is to implement a stream parser with the help of Gson JsonReader and JsonWriter classes. These classes operate on a JSON document as a sequence of tokens that are traversed in depth-first order. Because the streams operate on one token at a time, they impose minimal memory overhead. Here is example that implements both these classes and displays a list of countries sent to the mobile app from a MySql database using a Java Servlet.
|
|
Sample JSON data returned from the Java Servlet
{"countryList":[{"code":"ABW","name":"Aruba","continent":"North
America"},{"code":"AFG","name":"Afghanistan","continent":"Asia"},{"code":"AGO","name":"
Angola","continent":"Africa"},{"code":"AIA","name":"Anguilla","continent":"North
America"},{"code":"ALB","name":"Albania","continent":"Europe"},{"code":"AND","name":"
Andorra","continent":"Europe"},{"code":"ANT","name":"Netherlands
Antilles","continent":"North America"},{"code":"ARE","name":"United Arab
Emirates","continent":"Asia"},{"code":"ARG","name":"Argentina","continent":"South
America"},{"code":"ARM","name":"Armenia","continent":"Asia"},{"code":"ASM","name":"
American Samoa","continent":"Oceania"},{"code":"ATA","name":"Antarctica","continent":"Antarctica"},
{"code":"ATF","name":"French Southern territories","continent":"Antarctica"},{"code":"ATG","name":"Antigua and
Barbuda","continent":"North America"},{"code":"AUS","name":"Australia1","continent":"Oceania"},
...
...
Source for Java Servlet using JsonWriter class - CountryJSONData.java
package com.as400samplecode;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import com.as400samplecode.util.Country;
import com.google.gson.Gson;
import com.google.gson.stream.JsonWriter;
public class CountryJSONData extends HttpServlet {
private static final long serialVersionUID = 1L;
public CountryJSONData() {
super();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PrintWriter out = response.getWriter();
Connection conn = null;
PreparedStatement stmt = null;
String sql = null;
try {
Context ctx = (Context) new InitialContext().lookup("java:comp/env");
conn = ((DataSource) ctx.lookup("jdbc/mysql")).getConnection();
Gson gson = new Gson();
JsonWriter jsonWriter = new JsonWriter(out);
jsonWriter.beginObject();
sql = "Select * from country where code <> 'CIV'";
stmt = conn.prepareStatement(sql);
ResultSet rs = stmt.executeQuery();
jsonWriter.name("countryList");
jsonWriter.beginArray();
while(rs.next()){
Country country = new Country();
country.setCode(rs.getString("code").trim());
country.setName(rs.getString("name").trim());
country.setContinent(rs.getString("continent").trim());
gson.toJson(country, Country.class, jsonWriter);
}
jsonWriter.endArray();
rs.close();
stmt.close();
stmt = null;
conn.close();
conn = null;
jsonWriter.name("success").value(true);
jsonWriter.endObject();
jsonWriter.close();
}
catch(Exception e){
e.printStackTrace();
}
finally {
if (stmt != null) {
try {
stmt.close();
} catch (SQLException sqlex) {
// ignore -- as we can't do anything about it here
}
stmt = null;
}
if (conn != null) {
try {
conn.close();
} catch (SQLException sqlex) {
// ignore -- as we can't do anything about it here
}
conn = null;
}
}
}
}
Source for Country Object - Country.java
package com.as400samplecode;
public class Country {
String code = null;
String name = null;
String continent = null;
String region = null;
Double lifeExpectancy = null;
Double gnp = null;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getContinent() {
return continent;
}
public void setContinent(String continent) {
this.continent = continent;
}
public String getRegion() {
return region;
}
public void setRegion(String region) {
this.region = region;
}
public Double getLifeExpectancy() {
return lifeExpectancy;
}
public void setLifeExpectancy(Double lifeExpectancy) {
this.lifeExpectancy = lifeExpectancy;
}
public Double getGnp() {
return gnp;
}
public void setGnp(Double gnp) {
this.gnp = gnp;
}
}
Layout for the main activity - activity_main.xml
<?xml version="1.0" encoding="UTF-8"?>
<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" tools:context=".MainActivity">
<ListView android:id="@+id/countrylistView"
android:layout_width="match_parent" android:layout_height="wrap_content"
android:layout_alignParentLeft="true" android:layout_alignParentTop="true" />
</RelativeLayout>
Custom layout for ListView row - country_row.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="6dip" >
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:text="Code: "
android:textAppearance="?android:attr/textAppearanceMedium" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/textView1"
android:layout_below="@+id/textView1"
android:text="Name: "
android:textAppearance="?android:attr/textAppearanceMedium" />
<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/textView2"
android:layout_below="@+id/textView2"
android:text="Continent: "
android:textAppearance="?android:attr/textAppearanceMedium" />
<TextView
android:id="@+id/continent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/textView3"
android:layout_alignBottom="@+id/textView3"
android:layout_toRightOf="@+id/textView3"
android:text="TextView" />
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/textView3"
android:layout_toRightOf="@+id/textView3"
android:text="TextView" />
<TextView
android:id="@+id/code"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/textView2"
android:layout_alignLeft="@+id/name"
android:text="TextView" />
</RelativeLayout>
Application mainfest - AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.as400samplecode"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="15"
android:targetSdkVersion="16" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.as400samplecode.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Source for the main activity using JsonReader class - MainActivity.java
package com.as400samplecode;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.params.ConnManagerParams;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import com.google.gson.Gson;
import com.google.gson.stream.JsonReader;
import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Activity;
import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
public class MainActivity extends Activity {
private static final String LOG_TAG = "JSONStreamReader";
private ArrayList<Country> countryList = new ArrayList<Country>();
private boolean success = false;
private MyCustomAdapter dataAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//create an ArrayAdaptar from the String Array
dataAdapter = new MyCustomAdapter(this,
R.layout.country_row, countryList);
ListView listView = (ListView) findViewById(R.id.countrylistView);
// Assign adapter to ListView
listView.setAdapter(dataAdapter);
//URI to get the JSON stream data array of countries
String url = "http://demo.mysamplecode.com/Servlets_JSP/CountryJSONData";
new MyAsyncTask().execute(url);
}
private void displayCountries(){
//if the request was successful then notify the adapter to display the data
if(success){
dataAdapter.notifyDataSetChanged();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
//custom array adapter to display our custom row layout for the listview
private class MyCustomAdapter extends ArrayAdapter<Country> {
public MyCustomAdapter(Context context, int textViewResourceId,
ArrayList<Country> countryList) {
super(context, textViewResourceId, countryList);
}
private class ViewHolder {
TextView code;
TextView name;
TextView continent;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null) {
LayoutInflater vi = (LayoutInflater)getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
convertView = vi.inflate(R.layout.country_row, null);
holder = new ViewHolder();
holder.code = (TextView) convertView.findViewById(R.id.code);
holder.name = (TextView) convertView.findViewById(R.id.name);
holder.continent = (TextView) convertView.findViewById(R.id.continent);
convertView.setTag(holder);
}
else {
holder = (ViewHolder) convertView.getTag();
}
Country country = countryList.get(position);
holder.code.setText(country.getCode());
holder.name.setText(country.getName());
holder.continent.setText(country.getContinent());
return convertView;
}
}
//asynchronous task to get our JSON data with holding up the main thread
private class MyAsyncTask extends AsyncTask<String, Void, Void> {
private static final int REGISTRATION_TIMEOUT = 3 * 1000;
private static final int WAIT_TIMEOUT = 30 * 1000;
private final HttpClient httpclient = new DefaultHttpClient();
final HttpParams params = httpclient.getParams();
private boolean error = false;
protected Void doInBackground(String... urls) {
String URL = null;
try {
//URL passed to the AsyncTask
URL = urls[0];
HttpConnectionParams.setConnectionTimeout(params, REGISTRATION_TIMEOUT);
HttpConnectionParams.setSoTimeout(params, WAIT_TIMEOUT);
ConnManagerParams.setTimeout(params, WAIT_TIMEOUT);
HttpPost httpPost = new HttpPost(URL);
//Response from the Http Request
HttpResponse response = httpclient.execute(httpPost);
//Check the Http Request for success
StatusLine statusLine = response.getStatusLine();
if(statusLine.getStatusCode() == HttpStatus.SC_OK){
Gson gson = new Gson();
//create a new JSON reader from the response input stream
JsonReader jsonReader = new JsonReader(new InputStreamReader(response.getEntity().getContent(), "UTF-8"));
//begin parsing
jsonReader.beginObject();
//stay in loop as long as there are more data elements
while (jsonReader.hasNext()) {
//get the element name
String name = jsonReader.nextName();
if (name.equals("success")) {
success = jsonReader.nextBoolean();
}
//if the element name is the list of countries then start the array
else if(name.equals("countryList")){
jsonReader.beginArray();
while (jsonReader.hasNext()) {
//parse every element and convert that to a country object
Country country = gson.fromJson(jsonReader, Country.class);
//add the country object to the list
countryList.add(country);
}
jsonReader.endArray();
}
}
//end reader and close the stream
jsonReader.endObject();
jsonReader.close();
}
else{
//Closes the connection.
Log.w(LOG_TAG,statusLine.getReasonPhrase());
response.getEntity().getContent().close();
throw new IOException(statusLine.getReasonPhrase());
}
} catch (Exception e) {
Log.w(LOG_TAG,e );
error = true;
cancel(true);
}
return null;
}
protected void onCancelled() {
Log.e(LOG_TAG,"Error occured during data download");
}
protected void onPostExecute(Void unused) {
if (error) {
Log.e(LOG_TAG,"Data download ended abnormally!");
} else {
displayCountries();
}
}
}
}
Reference
No comments:
Post a Comment
NO JUNK, Please try to keep this clean and related to the topic at hand.
Comments are for users to ask questions, collaborate or improve on existing.