I have been creating an app recently that I am working on. The app simply shows the contents of a RSS feed in a ListView where the list items is clickable. It worked on Android 3.0 and below, but once i upgraded my app and device to 4.0 and above, it failed. I can see that I need to use a custom thread because of the NetworkOnMainThreadException, so I have chosen AsyncTask. I simply have a listview in my XML with id android:id/list and the code I use to load the feed looks like this:
//Load RSS news feed
// Initializing instance variables
headlines = new ArrayList();
links = new ArrayList();
try {
URL url = new URL("http://www.vg.no/rss/create.php?categories=25,10,49,26,23,20,21,12,32,30,34&keywords=85,98,1724,84,476,821,820,512,8317,343&limit=15");
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(false);
XmlPullParser xpp = factory.newPullParser();
// We will get the XML from an input stream
xpp.setInput(getInputStream(url), "UTF_8");
/* We will parse the XML content looking for the "<title>" tag which appears inside the "<item>" tag.
* However, we should take in consideration that the rss feed name also is enclosed in a "<title>" tag.
* As we know, every feed begins with these lines: "<channel><title>Feed_Name</title>...."
* so we should skip the "<title>" tag which is a child of "<channel>" tag,
* and take in consideration only "<title>" tag which is a child of "<item>"
*
* In order to achieve this, we will make use of a boolean variable.
*/
boolean insideItem = false;
// Returns the type of current event: START_TAG, END_TAG, etc..
int eventType = xpp.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG) {
if (xpp.getName().equalsIgnoreCase("item")) {
insideItem = true;
} else if (xpp.getName().equalsIgnoreCase("title")) {
if (insideItem)
headlines.add(xpp.nextText()); //extract the headline
} else if (xpp.getName().equalsIgnoreCase("link")) {
if (insideItem)
links.add(xpp.nextText()); //extract the link of article
}
}else if(eventType==XmlPullParser.END_TAG && xpp.getName().equalsIgnoreCase("item")){
insideItem=false;
}
eventType = xpp.next(); //move to next element
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
// Binding data
ArrayAdapter adapter = new ArrayAdapter(this,
android.R.layout.simple_list_item_1, headlines);
setListAdapter(adapter);
} catch (RuntimeException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Toast.makeText(getApplicationContext(), "Cannot load news, please check internett connection", Toast.LENGTH_LONG).show();
}
I have been trying to understand it; however, I cannot get it to work. If someone could please help me put all of this in AsyncTask and call the RSS refresh on onCreate() and on onClick() and explain how, it would be great.
Thanks!
this is a boilerplate inline AsyncTask method
new AsyncTask<Void, Void, Void>({
//initialize headlines here or outside of here or before this Asynctask
headlines = new ArrayList();
links = new ArrayList();
#Override
protected Void doInBackground(Void... params)
try {
URL url = new URL("http://www.vg.no/rss/create.php?categories=25,10,49,26,23,20,21,12,32,30,34&keywords=85,98,1724,84,476,821,820,512,8317,343&limit=15");
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(false);
XmlPullParser xpp = factory.newPullParser();
// We will get the XML from an input stream
xpp.setInput(getInputStream(url), "UTF_8");
/* We will parse the XML content looking for the "<title>" tag which appears inside the "<item>" tag.
* However, we should take in consideration that the rss feed name also is enclosed in a "<title>" tag.
* As we know, every feed begins with these lines: "<channel><title>Feed_Name</title>...."
* so we should skip the "<title>" tag which is a child of "<channel>" tag,
* and take in consideration only "<title>" tag which is a child of "<item>"
*
* In order to achieve this, we will make use of a boolean variable.
*/
boolean insideItem = false;
// Returns the type of current event: START_TAG, END_TAG, etc..
int eventType = xpp.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG) {
if (xpp.getName().equalsIgnoreCase("item")) {
insideItem = true;
} else if (xpp.getName().equalsIgnoreCase("title")) {
if (insideItem)
headlines.add(xpp.nextText()); //extract the headline
} else if (xpp.getName().equalsIgnoreCase("link")) {
if (insideItem)
links.add(xpp.nextText()); //extract the link of article
}
}else if(eventType==XmlPullParser.END_TAG && xpp.getName().equalsIgnoreCase("item")){
insideItem=false;
}
eventType = xpp.next(); //move to next element
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
#Override
protected Void onPostExecute() //UI THREAD STUFF , onPostExecute may need an argument, not sure yet though
ArrayAdapter adapter = new ArrayAdapter(this,
android.R.layout.simple_list_item_1, headlines);
setListAdapter(adapter);
}
}).execute();
good luck, you can put a loading progress bar in onPreExecute()
Related
So I'm working on an assignment where I have to create a TabLayout representing different categories of news, where the news is retrieved using the Bing search API and the JSON is parsed and used to populate the ListView in the three Fragments that make up the TabLayout. I'm also using a ViewPager.
My issue is that for some reason, the content of all three Fragments is the same... same article results. Why is this? I'm using Loader IDs, and the loader is initialized in the onActivityCreated() method. Is there a way I can load the articles relevant to the current Fragment when the user swipes over to that tab?
Here are the relevant methods of my Fragments. They're almost identical in each Fragment, with the exception of the LOADER_ID and CATEGORY_NAME values.
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Get a reference to the ConnectivityManager to check state of network connectivity
ConnectivityManager connMgr = (ConnectivityManager)
getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
// Get details on the currently active default data network
NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
// If there is a network connection, fetch data
if (networkInfo != null && networkInfo.isConnected()) {
// Get a reference to the LoaderManager, in order to interact with loaders.
LoaderManager loaderManager = getLoaderManager();
// Initialize the loader. Pass in the int ID constant defined above and pass in null for
// the bundle. Pass in this activity for the LoaderCallbacks parameter (which is valid
// because this activity implements the LoaderCallbacks interface).
loaderManager.initLoader(WorldFragment.LOADER_ID, null, this);
} else {
// Otherwise, display error
// First, hide loading indicator so error message will be visible
View loadingIndicator = getActivity().findViewById(R.id.loading_indicator);
loadingIndicator.setVisibility(View.GONE);
// Update empty state with no connection error message
mEmptyStateTextView.setText(R.string.no_internet_connection);
}
}
#Override
public Loader<List<Article>> onCreateLoader(int id, Bundle args) {
return new ArticleLoader(this.getContext(), WorldFragment.CATEGORY_NAME);
}
#Override
public void onLoadFinished(Loader<List<Article>> loader, List<Article> articles) {
// Hide loading indicator because the data has been loaded
View loadingIndicator = getActivity().findViewById(R.id.loading_indicator);
loadingIndicator.setVisibility(View.GONE);
// Set empty state text to display "No articles found."
mEmptyStateTextView.setText(R.string.no_articles);
// Clear the adapter of previous earthquake data
adapter.clear();
// If there is a valid list of {#link Earthquake}s, then add them to the adapter's
// data set. This will trigger the ListView to update.
if (articles != null && !articles.isEmpty()) {
adapter.addAll(articles);
}
}
#Override
public void onLoaderReset(Loader<List<Article>> loader) {
adapter.clear();
}
And here is the source for my ArticleLoader class. The "fetchArticleData" call is what retrieves the Article objects by parsing the JSON into Article objects.
public class ArticleLoader extends AsyncTaskLoader<List<Article>> {
private static final String LOG_TAG = ArticleLoader.class.getName();
private String category;
public ArticleLoader(Context context, String category) {
super(context);
this.category = category;
}
#Override
protected void onStartLoading() {
forceLoad();
}
#Override
public List<Article> loadInBackground() {
if (category == null) {
return null;
}
// Perform the network request, parse the response, and extract a list of earthquakes.
List<Article> articles = QueryUtils.fetchArticleData(category);
return articles;
}
}
As per request, here is the QueryUtils class.
public class QueryUtils {
private static final String LOG_TAG = QueryUtils.class.getSimpleName();
private static final String REQUEST_BASE_URL = "https://api.cognitive.microsoft.com/bing/v5.0/news/";
private static final String API_KEY = "redacted";
/**
* Create a private constructor because no one should ever create a {#link QueryUtils} object.
* This class is only meant to hold static variables and methods, which can be accessed
* directly from the class name QueryUtils (and an object instance of QueryUtils is not needed).
*/
private QueryUtils() {
}
/**
* Query the USGS dataset and return a list of {#link Article} objects.
*/
public static List<Article> fetchArticleData(String category) {
// Create URL object
HttpURLConnection conn = createUrlConnection(category);
// Perform HTTP request to the URL and receive a JSON response back
String jsonResponse = null;
try {
jsonResponse = makeHttpRequest(conn);
} catch (IOException e) {
Log.e(LOG_TAG, "Problem making the HTTP request.", e);
}
// Extract relevant fields from the JSON response and create a list of {#link Article}s
List<Article> articles = extractFeatureFromJson(jsonResponse);
// Return the list of {#link Article}s
return articles;
}
/**
* Returns new URL object from the given string URL.
*/
private static HttpURLConnection createUrlConnection(String category) {
URL url = null;
HttpURLConnection conn = null;
try {
url = new URL(QueryUtils.REQUEST_BASE_URL);
conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Category", category);
conn.setRequestProperty("Ocp-Apim-Subscription-Key", QueryUtils.API_KEY);
conn.setDoInput(true);
} catch (Exception e) {
Log.e(LOG_TAG, "Problem building the URL connection ", e);
}
return conn;
}
/**
* Make an HTTP request to the given URL and return a String as the response.
*/
private static String makeHttpRequest(HttpURLConnection conn) throws IOException {
String jsonResponse = "";
// If the URL is null, then return early.
if (conn == null) {
return jsonResponse;
}
InputStream inputStream = null;
try {
conn.connect();
// If the request was successful (response code 200),
// then read the input stream and parse the response.
if (conn.getResponseCode() == 200) {
inputStream = conn.getInputStream();
jsonResponse = readFromStream(inputStream);
} else {
Log.e(LOG_TAG, "Error response code: " + conn.getResponseCode());
}
} catch (IOException e) {
Log.e(LOG_TAG, "Problem retrieving the article JSON results.", e);
} finally {
if (conn != null) {
conn.disconnect();
}
if (inputStream != null) {
// Closing the input stream could throw an IOException, which is why
// the makeHttpRequest(URL url) method signature specifies than an IOException
// could be thrown.
inputStream.close();
}
}
return jsonResponse;
}
/**
* Convert the {#link InputStream} into a String which contains the
* whole JSON response from the server.
*/
private static String readFromStream(InputStream inputStream) throws IOException {
StringBuilder output = new StringBuilder();
if (inputStream != null) {
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, Charset.forName("UTF-8"));
BufferedReader reader = new BufferedReader(inputStreamReader);
String line = reader.readLine();
while (line != null) {
output.append(line);
line = reader.readLine();
}
}
return output.toString();
}
/**
* Return a list of {#link Article} objects that has been built up from
* parsing the given JSON response.
*/
private static List<Article> extractFeatureFromJson(String articleJSON) {
// If the JSON string is empty or null, then return early.
if (TextUtils.isEmpty(articleJSON)) {
return null;
}
// Create an empty ArrayList that we can start adding articles to
List<Article> articles = new ArrayList<>();
// Try to parse the JSON response string. If there's a problem with the way the JSON
// is formatted, a JSONException exception object will be thrown.
// Catch the exception so the app doesn't crash, and print the error message to the logs.
try {
// Create a JSONObject from the JSON response string
JSONObject baseJsonResponse = new JSONObject(articleJSON);
// Extract the JSONArray associated with the key called "features",
// which represents a list of features (or articles).
JSONArray articleArray = baseJsonResponse.getJSONArray("value");
// For each article in the articleArray, create an {#link Article} object
for (int i = 0; i < articleArray.length(); i++) {
// Get a single article at position i within the list of articles
JSONObject currentArticle = articleArray.getJSONObject(i);
// Extract the value for the key called "name"
String articleName = currentArticle.getString("name");
// Extract the value for the key called "url"
String articleSource = currentArticle.getString("url");
// Extract the value for the key called "image"
JSONObject imageObject = currentArticle.getJSONObject("image");
String imageSource = imageObject.getJSONObject("thumbnail").getString("contentUrl");
// Create a new {#link Article} object with the name, url, and image
// from the JSON response.
Article article = new Article(articleName, articleSource, imageSource);
// Add the new {#link Article} to the list of articles.
articles.add(article);
}
} catch (JSONException e) {
// If an error is thrown when executing any of the above statements in the "try" block,
// catch the exception here, so the app doesn't crash. Print a log message
// with the message from the exception.
Log.e("QueryUtils", "Problem parsing the article JSON results", e);
}
// Return the list of articles
return articles;
}
}
Does anyone know what I'm doing wrong?
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.
What is the best way to do that?
I want to parse the news and, then, filter them using something like keyword and find the match.
Someone has already done? And, it is lawful?
You can use rss feeds of google news url http://news.google.com/?output=rss it will return google rss news in the rss tag with html tags. Then either write custom code to read/parse the xml or using any existing RSS reading library like https://github.com/vgrec/SimpleRssReader
I have written a function to accomplish this which will return link and title of the random news each time.
public Document getNews() {
Document news = new Document();
URL rssUrl = null;
try {
rssUrl = new URL("https://news.google.com/rss");
} catch (MalformedURLException e) {
e.printStackTrace();
}
DocumentBuilder builder = null;
try {
builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
} catch (ParserConfigurationException e) {
e.printStackTrace();
}
org.w3c.dom.Document doc = null;
try {
doc = builder.parse(rssUrl.openStream());
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
NodeList items = doc.getElementsByTagName("item");
Element item = (Element) items.item(new Random().nextInt(items.getLength()));
news.append("title", getValue(item, "title"));
news.append("link", getValue(item, "link"));
return news;
}
private String getValue(Element parent, String nodeName) {
return parent.getElementsByTagName(nodeName).item(0).getFirstChild().toString();
}
I'm currently trying to parse some XML using XmlResourceParser and then show the information on a ListView using ArrayAdapter. The problem is, I don't get ant results.
My xml structure is like this:
<resources>
<categories>
<animal>
<word>
<english>Animal</english>
<french>Animal</french>
<spanish>Animal</spanish>
<portuguese>Animal</portuguese>
</word>
</animal>
<transportation></transportation>
<location></location>
<clothing></clothing>
<color></color>
<people></people>
<job></job>
<society></society>
<art></art>
<beverages></beverages>
<food></food>
<home></home>
<electronics></electronics>
<body></body>
<nature></nature>
<material></material>
<math></math>
<directions></directions>
<seasons></seasons>
<numbers></numbers>
<months></months>
<days></days>
<time></time>
<verbs></verbs>
<adjectives></adjectives>
</categories>
Now, each category will have different words on them but, at this moment I only want to get all the categories and show them in a ListView. To try and get all the categories I'm using the following code:
public class MainActivity extends ActionBarActivity {
private ListView list;
private ArrayList<String> arrayCategories;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
list = (ListView) findViewById(R.id.listView1);
try {
arrayCategories = parseCategories();
ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(
this,
R.id.listView1,
arrayCategories );
list.setAdapter(arrayAdapter);
} catch (IOException e) {
e.printStackTrace();
}
}
And the parseCategories function is the following:
private ArrayList<String> parseCategories() throws IOException {
ArrayList<String> categories = new ArrayList<String>();
XmlResourceParser parser = getResources().getXml(R.xml.database);
try {
int eventType = parser.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG) {
if (parser.getName().equals("categories")) {
categories.add(parser.nextText());
}
}
eventType = parser.next();
}
} catch (XmlPullParserException e) {
Log.e("XmlPullParserException", e.toString());
}
parser.close();
return categories;
}
I'm still a little bit unexperienced when it comes to parse xml, can you guys help me with this one?
The problem is there is no "text" in the categories element; there are, however, many child elements.
The real task can be trivially summed up as:
How to get the names of all elements which are direct children of the categories element?
This problem is made a little bit complicated by XmlPullParser (using a DOM and/or XPath would be much easier), but it is doable. The trick here is to "skip" the subtrees by keeping a little depth counter; recursive functions would also work.
int eventType = parser.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG
&& parser.getName().equals("categories")) { // catergories start
int depth = 0;
eventType = parser.next();
while (!(depth == 0 && eventType == XmlPullParser.END_TAG)) { // end categories
if (eventType == XmlPullParser.START_TAG) {
if (depth == 0) { // direct child of categories
categories.add(parser.getName());
}
depth++;
} else if (eventType == XmlPullParser.END_TAG) { // depth > 0
depth--;
}
eventType = parser.next();
}
}
eventType = parser.next();
}
I'm trying to extract data from an Xml file, I followed this tutorial:
XmlPullParser tutorial
And now have the following code:
public void parse(InputStream is) {
// create new Study object to hold data
try {
// get a new XmlPullParser object from Factory
XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
// set input source
parser.setInput(is, null);
// get event type
int eventType = parser.getEventType();
// process tag while not reaching the end of document
while(eventType != XmlPullParser.END_DOCUMENT) {
switch(eventType) {
// at start of document: START_DOCUMENT
case XmlPullParser.START_DOCUMENT:
break;
// at start of a tag: START_TAG
case XmlPullParser.START_TAG:
// get tag name
String tagName = parser.getName();
Log.i("AT START TAG","AT START TAG..."+tagName);
// if <study>, get attribute: 'id'
if(tagName.equalsIgnoreCase("Date")) {
Log.i("****PARSER INFO","TAG NAME="+tagName+"...."+parser.nextText());
eventDates.add(parser.nextText());
//study.mId = Integer.parseInt(parser.getAttributeValue(null, Study.ID));
}
// if <content>
else if(tagName.equalsIgnoreCase("Name")) {
Log.i("****PARSER INFO","TAG NAME="+tagName+"...."+parser.nextText());
performanceNames.add(parser.nextText());
//study.mContent = parser.nextText();
}
// if <topic>
else if(tagName.equalsIgnoreCase("RequestURL")) {
Log.i("****PARSER INFO","TAG NAME="+tagName+"...."+parser.nextText());
eventsURLS.add(parser.nextText());
//study.mTopic = parser.nextText();
}
break;
}
// jump to next event
eventType = parser.next();
}
// exception stuffs
} catch (XmlPullParserException e) {
//study = null;
} catch (IOException e) {
//study = null;
}
// return Study object
}
For some reason, the code within the IF statements is not running even though I have made sure the tag names do equal the strings above.
What am I doing wrong?