Browse through more Android tutorials. If you'd like to see a tutorial on any particular topic, do leave a comment in the wishlist page. We frequently post new tutorials along with app releases. You may subscribe to our newsletter to get all updates in your inbox.
Now you can get the latest Java source bundled with each app update. Install the app from Google Play and go to Settings > Extras.

«  Create Flappy Bird in Android Create a reminder/alarm app  »

Create a currency converter application using Yahoo! API

DownloadDownload

Keywords: ConnectivityManager HttpGet XmlPullParser SAXParser SimpleCursorAdapter SQLiteDatabase ContentProvider SQLiteQueryBuilder BroadcastReceiver IntentService AChartEngine Search Dialog Animation TableLayout

Contents

3 « Prev Page

9. The XmlPullParser

We'll make use of XmlPullParser to parse the symbols.xml file. Here is how to do it.
First, get an InputStream out of the XML file. Android provides a convenient method for doing this for a file present in the assets directory.


	InputStream is = context.getAssets().open(Constants.FILENAME_SYMBOLS);
					
Next, get a XmlPullParser instance from the factory and set the InputStream as input.
	XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
	XmlPullParser parser = factory.newPullParser();
	parser.setInput(is, null);
					
Finally, parse the XML file and populate the table simultaneously. Here is the logic.
	String elementName, elementValue;
	int eventType = parser.getEventType();
	
	while (eventType != XmlPullParser.END_DOCUMENT) {
		elementName = parser.getName();
		
		switch (eventType) {
		case XmlPullParser.START_TAG:
			// TODO create an instance of Symbol
			break;
			
		case XmlPullParser.TEXT:
			elementValue = parser.getText();
			// TODO set the value to Symbol
			break;
			
		case XmlPullParser.END_TAG:
			// TODO save the Symbol to DB
			break;
		}
		eventType = parser.next();
	}
					
Note that parser.next() is responsible for driving the parser that is why it's called a pull parser since your logic decides when to pull the next event.

It's a good practice to wrap multiple DB insert/update/delete calls in a transaction for better performance and to preserve data integrity. Also close any open I/O streams to release the resources once done with read/write operations.

10. The SAX Parser

We'll make use of SAX Parser to parse the quote.xml file.

SAX Parser is known to be faster than XmlPullParser and is a better choice when you have to parse a XML completely. However, for coding simplicity you may consider XmlPullParser and when you don't want to handle all the events.

The steps involved in using a SAX parser is similar to that of XmlPullParser. Here is the code.
	public boolean populate() {
		InputStream is = null;
		
		db.beginTransaction();		
		try {
			is = context.getAssets().open(FILENAME_QUOTE);
			populate(db, is);
			db.setTransactionSuccessful();
			
		} catch (Exception e) {
			return false;
		} finally {
			db.endTransaction();
			try {
				if (is!=null) is.close();
			} catch (IOException e) {}
		}
		return true;
	}
	
	private void populate(SQLiteDatabase db, InputStream is) 
					throws ParserConfigurationException, SAXException, IOException {
		SAXParserFactory factory = SAXParserFactory.newInstance();
		SAXParser parser = factory.newSAXParser();
		DefaultHandler handler = new QuoteHandler(db);
		parser.parse(is, handler);
	}
					
Next, implement a custom handler by extending DefaultHandler and override the callback methods.
	public class QuoteHandler extends DefaultHandler {
		private SQLiteDatabase db;
		private StringBuilder sb;
		private String elementName, elementValue;

		public QuoteHandler(SQLiteDatabase db) {
			super();
			this.db = db;
			sb = new StringBuilder();
		}

		@Override
		public void startElement(String uri, String localName, String qName, 
									Attributes attributes) throws SAXException {
			elementName = localName;
			// TODO create an instance of Quote
		}
		
		@Override
		public void characters(char[] ch, int start, int length) throws SAXException {
			sb.setLength(0);
			for (int i=start; i<start+length; i++) {
				sb.append(ch[i]);
			}
			elementValue = sb.toString();
			// TODO set the value to Quote
		}	

		@Override
		public void endElement(String uri, String localName, String qName) throws SAXException {
			// TODO save/update the Quote to DB		
		}	
	}
					
The QuoteHandler class can be used to update the quotes as well. The only thing that changes is the input source to the parser since we need to fetch latest quotes for updating the data.
	public static final String URL = "http://finance.yahoo.com/webservice/v1/symbols/allcurrencies/quote";
					
	// ... get SAXParser from SAXParserFactory
	parser.parse(new InputSource(URL), handler);
					

11. Calling RESTful service

If you recall we need to show Ask, Bid, etc. attributes of a currency pair. Yahoo! API provides a separate query for that where we can pass various parameters to specify the data we are interested in.

You may want to visit this page to know about what sort of data you can get from Yahoo!.

We'll next see how to make a HTTP Get request using HttpClient and handle the response to extract result out of it.
	String URL = "http://finance.yahoo.com/d/quotes.csv?e=.csv&f=sabd1l1t1&s=USDJPY=X"

	String result = null;
	ConnectivityManager connMgr = (ConnectivityManager) 
									context.getSystemService(Context.CONNECTIVITY_SERVICE);
	NetworkInfo netInfo = connMgr.getActiveNetworkInfo();
	HttpClient client = new DefaultHttpClient();
	
	boolean isConnected = netInfo != null 
							&& netInfo.getState() == NetworkInfo.State.CONNECTED
							//&& netInfo.getType() == ConnectivityManager.TYPE_WIFI
							;

	if (isConnected) {
		try {
			HttpGet request = new HttpGet(new URI(URL));
			HttpResponse response = client.execute(request);
			
			BasicResponseHandler handler = new BasicResponseHandler();
			result = handler.handleResponse(response);
			// TODO parse the result
			
		} catch (Exception e) {
			Log.e(TAG, e.getMessage(), e);
		}
	}
					
If you directly put the URL in browser address then you can see that it returns a CSV file with comma separated values in the order of attributes i.e. sabd1l1t1

We can query multiple currencies at the same time by separating them by a comma e.g. &s=USDJPY=X,EURCAD=X or by repeating the s parameter e.g. &s=USDJPY=X&s=EURCAD=X

12. Implement the Data Service

A currency converter app is supposed to show latest exchange rates and for that we'll develop a service which can update the DB values whenever requested. We'll make use of IntentService since it's designed to run in the background and stop itself when there are no more tasks in the queue.
Create a DataService class and make it extend IntentService and override onCreate() and onHandleIntent() methods. Also declare the service in the manifest so that it can be used later.
	<service android:name=".DataService"></service>
					
Here is an outline of the class.
	public class DataService extends IntentService {
		private SQLiteDatabase db;
		private ConnectivityManager connMgr;
		private HttpClient client;	

		public DataService() {
			super("DataService");
		}
		
		@Override
		public void onCreate() {
			super.onCreate();
			// TODO initialize db, connMgr, client 		
		}	

		@Override
		protected void onHandleIntent(Intent intent) {
			String[] pair = intent.getStringArrayExtra(Constants.EXTRA_PAIR);
			String result = null;
			BufferedReader br = null;
			
			// TODO check for network connectivity
			// TODO construct url from currency pair
			// TODO make REST call and get result
			
			if (result != null) {
				db.beginTransaction();	
				try {
					br = new BufferedReader(new StringReader(result));
					update(db, br);
					db.setTransactionSuccessful();
					broadcastStatus(Constants.STATUS_SUCCESS);
					
				} catch (Exception e) {
					Log.e(TAG, e.getMessage(), e);
				} finally {
					db.endTransaction();
					try {
						if (br!=null) br.close();
					} catch (IOException e) {}
				}
			}
			broadcastStatus(Constants.STATUS_FAILED);
		}
		
		/**
		 * "USDEUR=X",0.7802,0.7799,0.78,"3/29/2013","6:55pm"
		 */
		private void update(SQLiteDatabase db, BufferedReader br) throws IOException {
			String line;
			String[] tokens;
			while ((line=br.readLine()) != null) {
				tokens = line.split(",");

				// TODO set tokens to Quote and do update(db)
			}		
		}
		
		private void broadcastStatus(int status) {
			Intent intent = new Intent(Constants.ACTION_UPDATE);
			intent.putExtra(Constants.EXTRA_STATUS, status);
			sendBroadcast(intent);		
		}
	}
					

We are done with all the code that runs in the background and makes the app work. Next we'll develop the user interface of the application where we consume all the services and tasks we've developed so far.
Share the love:  

Next Page » 3

App Gen
App Name:
Project Name:
Package:
Screens:
Splash
Login
Help
Main
List  Grid  Pager
Detail
Settings
Options:
Action Bar
Navigation Drawer
Dummy Data
Generate
Free Apps