Android Development Tutorial 9

Android XmlPullParser TutorialWelcome to part 9 of my Android Development tutorial. Here is the complete package for my Android Stock Quote Picker.

In this tutorial I will focus on using the Android XmlPullParser for parsing an xml file. I’ll be getting data from the Yahoo Web Service just like last time. Google recommends the XmlPullParser over all others because it is fast, requires the least amount of memory and helps maintain battery life. With the video below and the code that follows you should have no problem using it in your own Android apps.

If you like videos like this, it helps to tell Google+ with a click here

Code From the Video

All of the code is available from the link above.

StockInfoActivity.java

package com.newthinktank.stockquotes;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URISyntaxException;

import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;

import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.util.Log;
import android.widget.TextView;

public class StockInfoActivity extends Activity {
	
	// Used to identify the app in the LogCat, so I
	// can output messages and debug the program
	
	private static final String TAG = "STOCKQUOTE";
	
	// Define the TextViews I use in activity_stock_info.xml
	
	TextView companyNameTextView;
	TextView yearLowTextView;
	TextView yearHighTextView;
	TextView daysLowTextView;
	TextView daysHighTextView;
	TextView lastTradePriceOnlyTextView;
	TextView changeTextView;
	TextView daysRangeTextView;
	
	// XML node keys
	static final String KEY_ITEM = "quote"; // parent node
	static final String KEY_NAME = "Name";
	static final String KEY_YEAR_LOW = "YearLow";
	static final String KEY_YEAR_HIGH = "YearHigh";
	static final String KEY_DAYS_LOW = "DaysLow";
	static final String KEY_DAYS_HIGH = "DaysHigh";
	static final String KEY_LAST_TRADE_PRICE = "LastTradePriceOnly";
	static final String KEY_CHANGE = "Change";
	static final String KEY_DAYS_RANGE = "DaysRange";
	
	// XML Data to Retrieve
	String name = "";
	String yearLow = "";
	String yearHigh = "";
	String daysLow = "";
	String daysHigh = "";
	String lastTradePriceOnly = "";
	String change = "";
	String daysRange = "";
	
	// Used to make the URL to call for XML data
	String yahooURLFirst = "http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20yahoo.finance.quote%20where%20symbol%20in%20(%22";
	String yahooURLSecond = "%22)&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys";
	
	// NEW STUFF
	
	// Holds values pulled from the XML document using XmlPullParser
	String[][] xmlPullParserArray = {{"AverageDailyVolume", "0"}, {"Change", "0"}, {"DaysLow", "0"},
			{"DaysHigh", "0"}, {"YearLow", "0"}, {"YearHigh", "0"},
			{"MarketCapitalization", "0"}, {"LastTradePriceOnly", "0"}, {"DaysRange", "0"},
			{"Name", "0"}, {"Symbol", "0"}, {"Volume", "0"},
			{"StockExchange", "0"}};
			
	int parserArrayIncrement = 0;
			
	// END OF NEW STUFF
	

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		
		// Creates the window used for the UI
		setContentView(R.layout.activity_stock_info);
		
		// Get the message from the intent that has the stock symbol
		Intent intent = getIntent();
		String stockSymbol = intent.getStringExtra(MainActivity.STOCK_SYMBOL);
		
		// Initialize TextViews
		companyNameTextView = (TextView) findViewById(R.id.companyNameTextView);
		yearLowTextView = (TextView) findViewById(R.id.yearLowTextView);
		yearHighTextView = (TextView) findViewById(R.id.yearHighTextView);
		daysLowTextView = (TextView) findViewById(R.id.daysLowTextView);
		daysHighTextView = (TextView) findViewById(R.id.daysHighTextView);
		lastTradePriceOnlyTextView = (TextView) findViewById(R.id.lastTradePriceOnlyTextView);
		changeTextView = (TextView) findViewById(R.id.changeTextView);
		daysRangeTextView = (TextView) findViewById(R.id.daysRangeTextView);
		
		// Sends a message to the LogCat
		Log.d(TAG, "Before URL Creation " + stockSymbol);
		
		// Create the YQL query
		final String yqlURL = yahooURLFirst + stockSymbol + yahooURLSecond;
		
		// The Android UI toolkit is not thread safe and must always be 
		// manipulated on the UI thread. This means if I want to perform
		// any network operations like grabbing xml data, I have to do it
		// in its own thread. The problem is that you can't write to the
		// GUI from outside the main activity. AsyncTask solves those problems
		
		new MyAsyncTask().execute(yqlURL);

	}
	
	// Use AsyncTask if you need to perform background tasks, but also need
	// to change components on the GUI. Put the background operations in
	// doInBackground. Put the GUI manipulation code in onPostExecute
	
	private class MyAsyncTask extends AsyncTask<String, String, String>{
		
		// String... arg0 is the same as String[] args
		protected String doInBackground(String... args) {
			
			// NEW STUFF
			
			try{
				
				Log.d("test","In XmlPullParser");
				
				// It is recommended to use the XmlPullParser because
				// it is faster and requires less memory then the DOM API
				
				// XmlPullParserFactory provides you with the ability to 
				// create pull parsers that parse XML documents
				
				XmlPullParserFactory factory = XmlPullParserFactory.newInstance();

				// Parser supports XML namespaces
				  factory.setNamespaceAware(true);

				  // Provides the methods needed to parse XML documents
				  XmlPullParser parser = factory.newPullParser(); 

				  // InputStreamReader converts bytes of data into a stream
				  // of characters
				  
				  parser.setInput(new InputStreamReader(getUrlData(args[0])));  

				  // Passes the parser and the first tag in the XML document
				  // for processing
				  
				  beginDocument(parser,"query");

				  // Get the currently targeted event type, which starts
				  // as START_DOCUMENT
				  
				  int eventType = parser.getEventType();

				  do{
					  
					// Cycles through elements in the XML document while
					// neither a start or end tag are found

					  nextElement(parser);

					  // Switch to the next element
					  
					  parser.next();
					  
					  // Get the current event type

					  eventType = parser.getEventType();

					  // Check if a value was found between 2 tags
					  
					  if(eventType == XmlPullParser.TEXT){
								  
						  // Get the text from between the tags
						  
						  String valueFromXML = parser.getText();
						  
						  // Store it in an array with the corresponding tag
						  // value
						  
						  xmlPullParserArray[parserArrayIncrement++][1] = valueFromXML;

					  }

				  } while (eventType != XmlPullParser.END_DOCUMENT) ;  
				
			}
			
			catch (ClientProtocolException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (XmlPullParserException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (URISyntaxException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}  
			
			finally {
		    }
			
			// END OF NEW STUFF
			
			return null;
		}
		
		// NEW STUFF
		
		public InputStream getUrlData(String url) throws URISyntaxException, 
        ClientProtocolException, IOException {

			// Used to get access to HTTP resources
			
			DefaultHttpClient client = new DefaultHttpClient();
			
			// Retrieves information from the URL
			
			HttpGet method = new HttpGet(new URI(url));
			
			// Gets a response from the client on whether the 
			// connection is stable
			
			HttpResponse res = client.execute(method);
			
			// An HTTPEntity may be returned using getEntity() which tells 
			// the system where the content is coming from
			
			return res.getEntity().getContent();
		}
		
		public final void beginDocument(XmlPullParser parser, String firstElementName) throws XmlPullParserException, IOException
	    {
			int type;
			
			// next() advances to the next element in the XML
			// document being a starting or ending tag, or a value
			// or the END_DOCUMENT
			
		    while ((type=parser.next()) != parser.START_TAG
		                   && type != parser.END_DOCUMENT) {
		            ;
		    }
		
		    // Throw an error if a start tag isn't found
		    
		    if (type != parser.START_TAG) {
		    	throw new XmlPullParserException("No start tag found");
		    }
		
		    // Verify that the tag passed in is the first tag in the XML
		    // document
		    
		    if (!parser.getName().equals(firstElementName)) {
		            throw new XmlPullParserException("Unexpected start tag: found " + parser.getName() +
		                    ", expected " + firstElementName);
		    }
	    }
		
		public final void nextElement(XmlPullParser parser) throws XmlPullParserException, IOException
		{
			int type;
			
			// Cycles through elements in the XML document while
			// neither a start or end tag are found
			
			while ((type=parser.next()) != parser.START_TAG
		                   && type != parser.END_DOCUMENT) {
		            ;
			}
		}
		
		// END OF NEW STUFF
		
		// Changes the values for a bunch of TextViews on the GUI
		protected void onPostExecute(String result){
			
			companyNameTextView.setText(xmlPullParserArray[9][1]);
			yearLowTextView.setText("Year Low: " + xmlPullParserArray[4][1]);
			yearHighTextView.setText("Year High: " + xmlPullParserArray[5][1]);
			daysLowTextView.setText("Days Low: " + xmlPullParserArray[2][1]);
			daysHighTextView.setText("Days High: " + xmlPullParserArray[3][1]);
			lastTradePriceOnlyTextView.setText("Last Price: " + xmlPullParserArray[7][1]);
			changeTextView.setText("Change: " + xmlPullParserArray[1][1]);
			daysRangeTextView.setText("Daily Price Range: " + xmlPullParserArray[8][1]);
			
		}

	}

}

44 Responses to “Android Development Tutorial 9”

  1. Mike says:

    Your tutorials are insanely great and I love it. I wonder if you could do a realistic and detailed debugging session using DDMS and debug threads and walking through code and showing a few good tricks along the way. Thanks and keep up the good work.

    Mike

    • Derek Banas says:

      Hi Mike, Thank you very much :) Yes I will cover DDMS and debugging. I’ll also make much more complicated apps soon. I just want to cover all of the basics first. I’m glad you are enjoying the tutorial

  2. Mike says:

    Yes, that’s what I am talking about, “much more complicated apps”. There are lots of beginner tutorials that implement trivial apps. More complicated apps, and better yet, more sophisticated apps will set you apart from the rest. I would appreciate it also if you did a tutorial concentrating on appearance and styling of the app. There is not much attention paid to styling, yet appearance is extremely important on a crowded android market. Thank you again for your priceless work.

    • Derek Banas says:

      I promise to cover all the topics that everyone is requesting. This tutorial will be very long and as complete as possible. I will definitely cover styling

  3. Waqas Asghar says:

    Hii Derek, how r u?? hope that u r fine. Awesome series brother. Can you please make a detail tutorial on multiscreen handling. I need to learn about it. can you??

  4. Waqas Asghar says:

    Hmm nice to hear this …. i will desperately wait for it because multiscreen handling is used in every project.

  5. Akuma says:

    Hi Derek, just wanna say thank you for those tutorials, I learned a lot =) ,can you please create a tutorials about voice commands that uses a database ,once again thank you =)

  6. wilfred says:

    your tutorials are amazing, and i thank you for that. sometimes the code fails to load due to manifest problem. i hope you can tell us more on how to edit the manifest. and again thanks a lot.

    • Derek Banas says:

      Thank you very much :) on occasion weird errors slip in that I never thought of. For example the tip calc messes up if people use a comma in the price amount instead of a decimal. I never thought about that.

      Ill see what I can do about any other weird errors. Feel free to post them here

  7. EYla says:

    Tnx, can’t wait to check out the next toturials.

  8. Vicki Borley says:

    receiving the following error:
    Description Resource Path Location Type
    Unable to resolve target ‘android-17′ StockQuotes Unknown Android Target Problem

  9. Vicki Borley says:

    Thanks, a restart of ecilpse seemed to resolve that error, now I’m getting:
    Description Resource Path Location Type
    The import android.support.v4.content.AsyncTaskLoader cannot be resolved StockInfoActivity.java /StockQuotes/src/com/newthinktank/stockquotes line 22 Java Problem

  10. Richard says:

    I was trying so hard to get the program from tutorial 8 to work. I spent several hours debugging the program, trying to get it to not crash. All I needed was was the beginning of this video. Haha, oh well.

    I love the tutorials though. The style is very much needed for this format. Thanks for making them!

  11. bihan says:

    thank you.this website is more important me.

  12. Chris says:

    I have tried for hours to get this to work but am not able to :(

    I started with 8 and then eventually changed over to 9 to use the XMLPullParser but still just crashes on start up. I made sure I had the tag set up in the manifest file and even added the id to the TableLayout in the activity_main.xml file that someone posted in the comments of tutorial 8.

    Coming from being an ASP, vbscript web developer, it was easy for me to see where my errors were and how to fix them. i am still too darn new in java to read these logs and understand how to fix general errors like the one I am getting:

    java.lang.runtimeexception unable to start activity componentinfo {com.example.stockquote/com.example.stockquote.MainActivity}:java.lang.nullpointerexception

    and down further it mentions 3 of the methods

    insertStockInScrollView (line 109)
    updateSavedStockList (line 73)
    onCreate (line 55)

    I am thinking it has to do with one of the nulls i have in the code, but i am not positive.

    any help would be appreciated!

    thanks ~

    • Derek Banas says:

      Have you tried downloading the Android Stock Picker Package? I have all the code wrapped up and ready for import. Tell me if you need any more help :)

      • Chris says:

        I did download all code from 8 and 9 earlier and couldn’t get that to work. I just downloaded the zip file you have above, imported and works perfectly…(strange?!) :)

        Now after comparing your code to mine here are the things that I changed (not sure if 1 or all are causing the errors):

        1) needed to add the StockInfoActivity activity to the manifest file.

        2) remove @Override from onCreate method in MainActivity.java

        3) changed i++ to ++i in updateSavedStockList method in MainActivity.java

        4) add android:stretchColumns=”yes” to TableLayout in activity_main.xml

        and then changed some minor things along the way to make my code look like yours…

        however I still get the same error so I have to be missing something silly :(

        I will keep digging…thanks for the reply!

        • Derek Banas says:

          I’m sorry about the confusion. It think the problem is that I made a package for each part of this tutorial being the stock app. Some times I have to make a judgement call, but I can see how having a bunch of apps with the same name that do something in a different way would cause conclusion.

          It seems like the major issue based on your list of problems is that the manifest isn’t updating. That seems to happen if Eclipse Indigo isn’t used.

  13. Phyo says:

    Hi Derek, Thanks for the tutorials. They are really great.

    Just a quick question, what is the main advantages in using XmlPullParser vs DOM method you used in previous tutorial (Tutorial 8)?

    Apart from different ways of doing things, are there any efficiency considerations? Which one is faster, take more memory/overhead etc.?

    Thanks, keep up the good work!!

    • Derek Banas says:

      Hi, You’re very welcome :)

      You’ll probably always prefer to use the XmlPullParser because it is easier to use then say SAX and it is the least processor intensive in comparison to all the other options. It is all about ease of use and extending battery life.

      I hope that helps
      Derek

  14. David says:

    Great tutorial Derek :)

    I think there is a problem in your code, however.

    XmlPullParser does not seem to take into account when the XML has just an empty closing tag (ex. ). This in turn leads to an array that is offset by the number of Texts missing. In my case, the daily low and daily high values are not filled in (http://pastebin.com/eHYQExUH). This in turn leads to xmlPullParserArray to be offset starting at [2][1]. On my version of the application I have 866 for the name of the stock which of course is the Volume (or the name offset by two).

    I thought I did something wrong but pasting your code from this page gives me the same error.

    Any ideas about how to avoid this type of error?

    Thanks again for all your tutorials and any light that you can shed on this subjet!

    • Derek Banas says:

      Thank you :) Did yahoo change the way they display the data. I’ll have to take a look at it to see if something changed. When I originally made the program worked. Sorry about any confusion.

      • David says:

        I have tried the query for a couple of days now and I always have received a ‘correct’ result. I’m thinking that it is during the weekend that the XML sends empty closing tags for DaysLow and DaysHigh, which would make sense as there would be no day low or high during the weekend. So in general, I think that your code works perfect, except for maybe the weekend. I will let you know if this is really the case when I retest this weekend. In any event, thanks again!

  15. Christoffer says:

    I tried to modify our stockapp.

    http://pastebin.com/7q4Wdyrh

    I tried different approaches. But it didn’t work out for me.

    If the stock doesn’t exists, I want an alertbox to show.

  16. AASHRAI RAVOORU says:

    many of friends have told me that SAXparser is more easier to implemnt and understand can you please upload the code for the same project using SAXparser

    • Derek Banas says:

      I’ll go back and cover it in the future, but Google recommends that the XmlPullParser be used because of a couple reasons, but the big one is that it isn’t as much a drain on the battery which is very important.

  17. Marc says:

    When I try to get a quote, the ‘Company Name’ is displayed near the top of the page, then, about 2/3 of the way down, the ‘Year Low’ and ‘Year High’ are displayed, but that is all. None of the rest of the information is displayed. When I looked in LogCat, it shows ‘skipped frames’ (see below). Is this what is causing my display problems? If so, how do I fix it?

    LogCat output:

    12-22 07:19:25.059: E/Trace(1200): error opening trace file: No such file or directory (2)
    12-22 07:19:26.139: D/dalvikvm(1200): GC_FOR_ALLOC freed 67K, 8% free 2550K/2764K, paused 37ms, total 41ms
    12-22 07:19:26.519: D/dalvikvm(1200): GC_CONCURRENT freed 65K, 7% free 2885K/3092K, paused 15ms+52ms, total 156ms
    12-22 07:19:26.669: D/gralloc_goldfish(1200): Emulator without GPU emulation detected.
    12-22 07:19:32.759: D/STOCKQUOTE(1200): Before URL Creation appl
    12-22 07:19:50.079: I/Choreographer(1200): Skipped 49 frames! The application may be doing too much work on its main thread.
    12-22 07:19:53.759: D/STOCKQUOTE(1200): Before URL Creation msft
    12-22 07:19:53.909: D/dalvikvm(1200): GC_CONCURRENT freed 187K, 10% free 3112K/3444K, paused 2ms+16ms, total 106ms
    12-22 07:33:02.538: I/Choreographer(1200): Skipped 39 frames! The application may be doing too much work on its main thread.
    12-22 07:33:07.369: D/STOCKQUOTE(1200): Before URL Creation appl
    12-22 07:33:14.139: I/Choreographer(1200): Skipped 44 frames! The application may be doing too much work on its main thread.
    12-22 07:33:22.209: D/dalvikvm(1200): GC_CONCURRENT freed 368K, 15% free 3130K/3644K, paused 3ms+5ms, total 104ms
    12-22 07:33:23.859: D/STOCKQUOTE(1200): Before URL Creation aapl
    12-22 07:33:49.849: W/IInputConnectionWrapper(1200): showStatusIcon on inactive InputConnection

  18. Tom says:

    Hi Derek,

    I followed everything from this stock app, rechecked your code over and over and redid the tutorials, first one that would not work for me, can I zip the file to you and you tell me where my problem is, so many hours trying to make it work with no avail.

    • Derek Banas says:

      Hi,

      Maybe something changed on Yahoos end? I’ll have to look into it. All the information presented here though should allow for a fix in the code. Probably a tag name changed I’m guessing. Sorry about that.

  19. Tom says:

    Hi Derek we are in a full time Android college class here at the international English academy in Aarhus, Denmark. Two other of my classmates just completed this one too, and had the same errors, not able to make the stockquote app work. Same coding as yours, so something is rotten in Denmark. HaHa. Do you accept zip files to look at, maybe there is a problem with yahoo like you said.

    • Derek Banas says:

      Hi Tom, It seems like Yahoo may be blocked in certain places because I’ve had people tell me that it didn’t work at work and then it did work from home? I’ll look into whether the feed from yahoo has changed as well. If Yahoo changes their tags that will break the app. Sorry about that.

  20. Marc says:

    Well, the problem must, indeed, be with the emulator. I decided to verify this by installing the app on my tablet. Other than the fact that Eclipse could not verify my tablet (a Nook HD running Android For Nook v. 4.2.2) and I had to side load the app, everything worked just fine on the tablet once it was installed.

    Any ideas on why Eclipse would not directly install the app on my tablet?

  21. Derek Banas says:

    Sorry but I’m not sure. I’m not sure if I mentioned it before but in part 26 of my Android tutorial I cover how to install the new Android software. That may help

  22. Marc says:

    You did mention it and I checked the files at http://developer.android.com/sdk/index.html. What I have is the most recent version of the ADT bundle, according to that site.

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title="" rel=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Google+