What is the best way to use one instance of a Class in multiple Fragments? (or just a method of it)
In my Application I have a Class RangeFetcher which downloads an XML-file and parses it into a model class Item. RangeFetcher holds an ArrayList<Item> items containing the whole XML-file parsed into instances of the model.
I'm creating the Fragment ShopFragment from my starting activity ShopMainViewScreen. In this Fragment I instantiate a RangeFetcher. Since this is a pretty expensive operation in terms of data volume and computing, I would like to pass this instance of RangeFetcher to the other three Fragments I have.
This method getItems is basically all I need in the fragments.
public class RangeFetcher extends AsyncTask {
ArrayList <Item> items = new ArrayList();
//filling the List...
public ArrayList <Item> getItems() {
return items;
}
}
What i took into consideration up to now:
I thought about using a bundle and passing the ArrayList in the arguments to pass the instance from one Fragment to another, but that feels kinda clunky. Could this cause trouble regarding the back-stack?
making the method getItems static? I think it would be feasible since the RangeFetcher will always produce exactly the same output for all the fragments. (am I missing something here?)
Do you have any other suggestions on how to access this instance from multiple fragments?
RangeFetcher Class
package com.XXX;
import android.os.AsyncTask;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
//I know using a switch statement would probably be more fitting here.
//Furthermore I didn't know how to wait for the background task to
//finish so i just introduced a boolean "processing". When this boolean
//is set to false, i continue with my tasks on the fragments.
//So feel free to suggest some improvements to my code ^^
public class RangeFetcher extends AsyncTask {
URL url;
ArrayList < Item > items = new ArrayList();
boolean processing = false;
Item i;
protected Object doInBackground(Object[] objects) {
processing = true;
try {
url = new URL("XXXX");
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(false);
XmlPullParser xpp = factory.newPullParser();
xpp.setInput(getInputStream(url), "UTF_8");
boolean insideItem = false;
boolean priceSet = false;
int eventType = xpp.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG) {
if (xpp.getName().equalsIgnoreCase("item")) {
insideItem = true;
i = new Item();
} else if (xpp.getName().equalsIgnoreCase("g:id")) {
if (insideItem)
i.setmID(xpp.nextText());
} else if (xpp.getName().equalsIgnoreCase("title")) {
if (insideItem)
i.setmTitle(xpp.nextText());
} else if (xpp.getName().equalsIgnoreCase("description")) {
if (insideItem)
i.setmDescription(xpp.nextText());
} else if (xpp.getName().equalsIgnoreCase("g:product_type")) {
if (insideItem) {
String fullCategory = xpp.nextText();
if (fullCategory.contains(" ")) {
fullCategory = fullCategory.substring(0, fullCategory.indexOf(" "));
}
i.setmProductType(fullCategory);
}
} else if (xpp.getName().equalsIgnoreCase("g:image_link")) {
if (insideItem)
i.setmPictureLink(xpp.nextText());
} else if (xpp.getName().equalsIgnoreCase("g:condition")) {
if (insideItem)
i.setmCondition(xpp.nextText());
} else if (xpp.getName().equalsIgnoreCase("g:availability")) {
if (insideItem)
i.setmAvailability(xpp.nextText());
} else if (xpp.getName().equalsIgnoreCase("g:price") && priceSet == false) {
if (insideItem)
i.setmPrice(xpp.nextText());
priceSet = true;
} else if (xpp.getName().equalsIgnoreCase("g:brand")) {
if (insideItem)
i.setmBrand(xpp.nextText());
} else if (xpp.getName().equalsIgnoreCase("g:mpn")) {
if (insideItem)
i.setmMpn(xpp.nextText());
} else if (xpp.getName().equalsIgnoreCase("g:country")) {
if (insideItem)
i.setmShippingCountry(xpp.nextText());
} else if (xpp.getName().equalsIgnoreCase("g:service")) {
if (insideItem)
i.setmService(xpp.nextText());
} else if (xpp.getName().equalsIgnoreCase("g:price")) {
if (insideItem)
i.setmShippingCosts(xpp.nextText());
} else if (xpp.getName().equalsIgnoreCase("g:pubDate")) {
if (insideItem)
i.setMpubDate(xpp.nextText());
}
} else if (eventType == XmlPullParser.END_TAG && xpp.getName().equalsIgnoreCase("item")) {
insideItem = false;
items.add(i);
priceSet = false;
}
eventType = xpp.next(); //move to next element
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
processing = false;
return items;
}
private InputStream getInputStream(URL url) {
try {
return url.openConnection().getInputStream();
} catch (IOException e) {
return null;
}
}
public ArrayList < Item > getItems() {
while (processing == true) {}
return items;
}
public Boolean processing() {
return processing;
}
}
I have this XML
<CurrencyExchangeMap>
<CurrencyExchangePoint>
<Address>addr 3</Address>
<Latitude>41.6940265</Latitude>
<Longitude>44.7985044</Longitude>
</CurrencyExchangePoint>
<CurrencyExchangePoint>
<Address>addr 4</Address>
<Latitude>41.7024424</Latitude>
<Longitude>44.8058617</Longitude>
</CurrencyExchangePoint>
<CurrencyExchangePoint>
<Address>addr 5</Address>
<Latitude>41.6954418</Latitude>
<Longitude>44.7046725</Longitude>
</CurrencyExchangePoint>
</CurrencyExchangeMap>
It has 1000+ CurrencyExchangePoint But when I'm parsin them it returns ONLY 167 item. What's wrong? I've checked xml file in stylus studio and by myself and didn't find any error.
List<MapLT> mapLTs = null;
try {
XMLPullParserHandler parserHandler = new XMLPullParserHandler();
mapLTs = parserHandler.parse(getAssets().open("ltlg.xml"));
for (MapLT item : mapLTs) {
LatLng lt = new LatLng(item.getLatitude(), item.getLongitude());
String title = item.getAddress();
googleMap.addMarker(new MarkerOptions().position(lt).title(title));
}
} catch (Exception ex) {
ex.printStackTrace();
}
and my xmlpullparse class:
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;
public class XMLPullParserHandler {
List<MapLT> mapLTList;
private MapLT mapLT;
private String text;
public XMLPullParserHandler() {
mapLTList = new ArrayList<MapLT>();
}
public List<MapLT> getMapLT(){
return mapLTList;
}
public List<MapLT> parse(InputStream is){
XmlPullParserFactory factory = null;
XmlPullParser parser = null;
try{
factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(true);
parser = factory.newPullParser();
parser.setInput(is, null);
int eventType = parser.getEventType();
while(eventType != XmlPullParser.END_DOCUMENT){
String tagname = parser.getName();
switch (eventType){
case XmlPullParser.START_TAG:
if(tagname.equalsIgnoreCase("CurrencyExchangePoint")){
mapLT = new MapLT();
}
break;
case XmlPullParser.TEXT:
text = parser.getText();
break;
case XmlPullParser.END_TAG:
if(tagname.equalsIgnoreCase("CurrencyExchangePoint")){
mapLTList.add(mapLT);
} else if(tagname.equalsIgnoreCase("Address")){
mapLT.setAddress(text);
} else if(tagname.equalsIgnoreCase("Latitude")){
mapLT.setLatitude(Float.parseFloat(text));
} else if(tagname.equalsIgnoreCase("Longitude")){
mapLT.setLongitude(Float.parseFloat(text));
}
break;
default:
break;
}
eventType = parser.next();
}
}catch (Exception ex){
ex.printStackTrace();
}
return mapLTList;
}
}
I can't spot a problem in your code, so my strategy would be to add logs.
Start by printing all the event tags and tag names you get.
Problem was that value of one tag from 6500 was empty..
And it took my 4 hour
I have found several questions of the name question, but can't get any of them to work. What I want is to get the url of the media:thumbnail tag:
<media:thumbnail width="144" height="81" url="http://c.files.bbci.co.uk/6013/production/_88159542_3e6f2bc3-16a3-407d-9e07-62bae1fa755e.jpg"/>
Above the example of such tag
private void handleText(String text) {
String xmlText = text;
if (currentEntry != null && currentTag != null) {
if (currentTag.equals(TITLE)) {
currentEntry.setTitle(xmlText);
} else if (currentTag.equals(DESC)) {
currentEntry.setDescription(xmlText);
} else if (currentTag.equals(LINK)) {
currentEntry.setLink(xmlText);
} else if (currentTag.equals(IMAGE)) {
currentEntry.setImage("test");
}
}
}
I tried several things as:
xpp.getAttributeValue(null, "url"); and set the image as that. However I noticed that I am not even getting in that else if clause. I tried several values on the IMAGE variable like:
media:thumbnail
media
thumbnail
I have also set namespace aware:
factory.setNamespaceAware(true);
What am I doing wrong?
parser:
XmlPullParser xpp;
int eventType;
protected List<Entry> doInBackground(String... string) {
try {
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(true);
xpp = factory.newPullParser();
xpp.setInput(getInputStream(new URL("http://feeds.bbci.co.uk/news/technology/rss.xml?edition=uk")), "UTF_8");
eventType = xpp.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG) {
handleStartTag(xpp.getName());
} else if (eventType == XmlPullParser.END_TAG) {
currentTag = null;
} else if (eventType == XmlPullParser.TEXT) {
handleText(xpp.getText());
}
eventType = xpp.next();
}
} catch (Resources.NotFoundException e) {
Log.d(LOGTAG, e.getMessage());
} catch (XmlPullParserException e) {
Log.d(LOGTAG, e.getMessage());
} catch (IOException e) {
Log.d(LOGTAG, e.getMessage());
}
return entries;
}
I fixed it. I systemed out the start tags it was parsing and it showed up as: thumbnail. So I changed my IMAGE constant to have the value of "thumbnail". It never came in the thumbnail clause since the handleText method only handles found text in a tag. Since media:thumbnail has no text only attributes with values I needed to handle it in the handleStartTag method. There I could say if the current tag name equals "thumbnail" get the attribute value of url and setImage as that value.
I have a Java class that is going to have a number of inner classes. This is done for organization and to keep things in a separate file.
public class PUCObjects
{
public static class PUCNewsItem
{
public String title;
public String summary;
public String body;
public String url;
public String imageUrl;
}
}
I am then trying to create a new instance of that inner class (doing this in another class that parses some remote XML), but for some reason it doesn't seem to get created:
public static ArrayList<PUCObjects.PUCNewsItem> getPUCNews() throws IOException {
String url = "http://api.puc.edu/news/list?key="+API_KEY+"&count=30";
InputStream is = downloadUrl(url);
XmlPullParserFactory pullParserFactory;
try {
pullParserFactory = XmlPullParserFactory.newInstance();
XmlPullParser parser = pullParserFactory.newPullParser();
parser.setInput(is, null);
ArrayList<PUCObjects.PUCNewsItem> items = null;
int eventType = parser.getEventType();
PUCObjects.PUCNewsItem item = null;
Log.d("Debug: ", "Start: "+url);
while (eventType != XmlPullParser.END_DOCUMENT){
String name = null;
switch (eventType){
case XmlPullParser.START_DOCUMENT:
items = new ArrayList<PUCObjects.PUCNewsItem>();
break;
case XmlPullParser.START_TAG:
name = parser.getName();
//Log.d("Start Tag Name: ", parser.getName()+" === "+name);
if (name == "item"){
Log.d("Debug: ", "Item");
item = new PUCObjects.PUCNewsItem();
} else if (item != null){
Log.d("Debug: ", "Item is not NULL 2");
if (name == "title"){
Log.d("Title: ", parser.nextText());
item.title = parser.nextText();
} else if (name == "summary"){
item.summary = parser.nextText();
} else if (name == "body_text"){
item.body = parser.nextText();
}
}
break;
case XmlPullParser.END_TAG:
name = parser.getName();
if (name.equalsIgnoreCase("item") && item != null) {
Log.d("Debug: ", "ADD ITEM");
items.add(item);
}
break;
}//end switch
eventType = parser.next();
}//end while
Log.d("Debug: ", "Done");
return items;
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}//end
I am trying to create the object like item = new PUCObjects.PUCNewsItem(); but it seems to always be null.
Is there a reason why this is object isn't getting created?
Problem is String comparison. Your if statement is not resulting to true due to == check.
if (name == "item"){
You need to use equals() method instead of == when comparing Objects/Strings. Read this thread for more information on eqauals() vs ==
I am trying to pull some XHTML out of an RSS feed so I can place it in a WebView. The RSS feed in question has a tag called <content> and the characters inside the content are XHTML. (The site I'm paring is a blogger feed)
What is the best way to try to pull this content? The < characters are confusing my parser. I have tried both DOM and SAX but neither can handle this very well.
Here is a sample of the XML as requested. In this case, I want basically XHTML inside the content tag to be a string. <content> XHTML </content>
Edit: based on ignyhere's suggestion I have tried XPath, but I am still having the same issue. Here is a pastebin sample of my tests.
It's not pretty, but this is (the essence of) what I use to parse an ATOM feed from Blogger using XmlPullParser. The code is pretty icky, but it is from a real app. You can probably get the general flavor of it, anyway.
final String TAG_FEED = "feed";
public int parseXml(Reader reader) {
XmlPullParserFactory factory = null;
StringBuilder out = new StringBuilder();
int entries = 0;
try {
factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(true);
XmlPullParser xpp = factory.newPullParser();
xpp.setInput(reader);
while (true) {
int eventType = xpp.next();
if (eventType == XmlPullParser.END_DOCUMENT) {
break;
} else if (eventType == XmlPullParser.START_DOCUMENT) {
out.append("Start document\n");
} else if (eventType == XmlPullParser.START_TAG) {
String tag = xpp.getName();
// out.append("Start tag " + tag + "\n");
if (TAG_FEED.equalsIgnoreCase(tag)) {
entries = parseFeed(xpp);
}
} else if (eventType == XmlPullParser.END_TAG) {
// out.append("End tag " + xpp.getName() + "\n");
} else if (eventType == XmlPullParser.TEXT) {
// out.append("Text " + xpp.getText() + "\n");
}
}
out.append("End document\n");
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
// return out.toString();
return entries;
}
private int parseFeed(XmlPullParser xpp) throws XmlPullParserException, IOException {
int depth = xpp.getDepth();
assert (depth == 1);
int eventType;
int entries = 0;
xpp.require(XmlPullParser.START_TAG, null, TAG_FEED);
while (((eventType = xpp.next()) != XmlPullParser.END_DOCUMENT) && (xpp.getDepth() > depth)) {
// loop invariant: At this point, the parser is not sitting on
// end-of-document, and is at a level deeper than where it started.
if (eventType == XmlPullParser.START_TAG) {
String tag = xpp.getName();
// Log.d("parseFeed", "Start tag: " + tag); // Uncomment to debug
if (FeedEntry.TAG_ENTRY.equalsIgnoreCase(tag)) {
FeedEntry feedEntry = new FeedEntry(xpp);
feedEntry.persist(this);
entries++;
// Log.d("FeedEntry", feedEntry.title); // Uncomment to debug
// xpp.require(XmlPullParser.END_TAG, null, tag);
}
}
}
assert (depth == 1);
return entries;
}
class FeedEntry {
String id;
String published;
String updated;
// Timestamp lastRead;
String title;
String subtitle;
String authorName;
int contentType;
String content;
String preview;
String origLink;
String thumbnailUri;
// Media media;
static final String TAG_ENTRY = "entry";
static final String TAG_ENTRY_ID = "id";
static final String TAG_TITLE = "title";
static final String TAG_SUBTITLE = "subtitle";
static final String TAG_UPDATED = "updated";
static final String TAG_PUBLISHED = "published";
static final String TAG_AUTHOR = "author";
static final String TAG_CONTENT = "content";
static final String TAG_TYPE = "type";
static final String TAG_ORIG_LINK = "origLink";
static final String TAG_THUMBNAIL = "thumbnail";
static final String ATTRIBUTE_URL = "url";
/**
* Create a FeedEntry by pulling its bits out of an XML Pull Parser. Side effect: Advances
* XmlPullParser.
*
* #param xpp
*/
public FeedEntry(XmlPullParser xpp) {
int eventType;
int depth = xpp.getDepth();
assert (depth == 2);
try {
xpp.require(XmlPullParser.START_TAG, null, TAG_ENTRY);
while (((eventType = xpp.next()) != XmlPullParser.END_DOCUMENT)
&& (xpp.getDepth() > depth)) {
if (eventType == XmlPullParser.START_TAG) {
String tag = xpp.getName();
if (TAG_ENTRY_ID.equalsIgnoreCase(tag)) {
id = Util.XmlPullTag(xpp, TAG_ENTRY_ID);
} else if (TAG_TITLE.equalsIgnoreCase(tag)) {
title = Util.XmlPullTag(xpp, TAG_TITLE);
} else if (TAG_SUBTITLE.equalsIgnoreCase(tag)) {
subtitle = Util.XmlPullTag(xpp, TAG_SUBTITLE);
} else if (TAG_UPDATED.equalsIgnoreCase(tag)) {
updated = Util.XmlPullTag(xpp, TAG_UPDATED);
} else if (TAG_PUBLISHED.equalsIgnoreCase(tag)) {
published = Util.XmlPullTag(xpp, TAG_PUBLISHED);
} else if (TAG_CONTENT.equalsIgnoreCase(tag)) {
int attributeCount = xpp.getAttributeCount();
for (int i = 0; i < attributeCount; i++) {
String attributeName = xpp.getAttributeName(i);
if (attributeName.equalsIgnoreCase(TAG_TYPE)) {
String attributeValue = xpp.getAttributeValue(i);
if (attributeValue
.equalsIgnoreCase(FeedReaderContract.FeedEntry.ATTRIBUTE_NAME_HTML)) {
contentType = FeedReaderContract.FeedEntry.CONTENT_TYPE_HTML;
} else if (attributeValue
.equalsIgnoreCase(FeedReaderContract.FeedEntry.ATTRIBUTE_NAME_XHTML)) {
contentType = FeedReaderContract.FeedEntry.CONTENT_TYPE_XHTML;
} else {
contentType = FeedReaderContract.FeedEntry.CONTENT_TYPE_TEXT;
}
break;
}
}
content = Util.XmlPullTag(xpp, TAG_CONTENT);
extractPreview();
} else if (TAG_AUTHOR.equalsIgnoreCase(tag)) {
// Skip author for now -- it is complicated
int authorDepth = xpp.getDepth();
assert (authorDepth == 3);
xpp.require(XmlPullParser.START_TAG, null, TAG_AUTHOR);
while (((eventType = xpp.next()) != XmlPullParser.END_DOCUMENT)
&& (xpp.getDepth() > authorDepth)) {
}
assert (xpp.getDepth() == 3);
xpp.require(XmlPullParser.END_TAG, null, TAG_AUTHOR);
} else if (TAG_ORIG_LINK.equalsIgnoreCase(tag)) {
origLink = Util.XmlPullTag(xpp, TAG_ORIG_LINK);
} else if (TAG_THUMBNAIL.equalsIgnoreCase(tag)) {
thumbnailUri = Util.XmlPullAttribute(xpp, tag, null, ATTRIBUTE_URL);
} else {
#SuppressWarnings("unused")
String throwAway = Util.XmlPullTag(xpp, tag);
}
}
} // while
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
assert (xpp.getDepth() == 2);
}
}
public static String XmlPullTag(XmlPullParser xpp, String tag)
throws XmlPullParserException, IOException {
xpp.require(XmlPullParser.START_TAG, null, tag);
String itemText = xpp.nextText();
if (xpp.getEventType() != XmlPullParser.END_TAG) {
xpp.nextTag();
}
xpp.require(XmlPullParser.END_TAG, null, tag);
return itemText;
}
public static String XmlPullAttribute(XmlPullParser xpp,
String tag, String namespace, String name)
throws XmlPullParserException, IOException {
assert (!TextUtils.isEmpty(tag));
assert (!TextUtils.isEmpty(name));
xpp.require(XmlPullParser.START_TAG, null, tag);
String itemText = xpp.getAttributeValue(namespace, name);
if (xpp.getEventType() != XmlPullParser.END_TAG) {
xpp.nextTag();
}
xpp.require(XmlPullParser.END_TAG, null, tag);
return itemText;
}
I'll give you a hint: None of the return values matter. The data is saved into a database by a method (not shown) called at this line:
feedEntry.persist(this);
I would attempt to attack it with XPath. Would something like this work?
public static String parseAtom (InputStream atomIS)
throws Exception {
// Below should yield the second content block
String xpathString = "(//*[starts-with(name(),"content")])[2]";
// or, String xpathString = "//*[name() = 'content'][2]";
// remove the '[2]' to get all content tags or get the count,
// if needed, and then target specific blocks
//String xpathString = "count(//*[starts-with(name(),"content")])";
// note the evaluate expression below returns a glob and not a node set
XPathFactory xpf = XPathFactory.newInstance ();
XPath xpath = xpf.newXPath ();
XPathExpression xpathCompiled = xpath.compile (xpathString);
// use the first to recast and evaluate as NodeList
//Object atomOut = xpathCompiled.evaluate (
// new InputSource (atomIS), XPathConstants.NODESET);
String atomOut = xpathCompiled.evaluate (
new InputSource (atomIS), XPathConstants.STRING);
System.out.println (atomOut);
return atomOut;
}
I can see your problem here, the reason why these parsers are not producing the correct result is because contents of your <content> tag are not wrapped into <![CDATA[ ]]>, what I would do until I find more adequate solution I'd use quick and dirty trick :
private void parseFile(String fileName) throws IOException {
String line;
BufferedReader br = new BufferedReader(new FileReader(new File(fileName)));
StringBuilder sb = new StringBuilder();
boolean match = false;
while ((line = br.readLine()) != null) {
if(line.contains("<content")){
sb.append(line);
sb.append("\n");
match = true;
continue;
}
if(match){
sb.append(line);
sb.append("\n");
match = false;
}
if(line.contains("</content")){
sb.append(line);
sb.append("\n");
}
}
System.out.println(sb.toString());
}
This will give you all content in String. You can optionaly seperate them by slightly modyfiying this method or if you don't need actual <content> you can filter that out as well.