I have been trying to parse XML files using Asynctask, following [1] and [2] tutorials. I have implemented a class in my Activity as follows:
private class GetRoutes extends AsyncTask<String, Void, String[]> {
#Override
protected String[] doInBackground(String... urls) {
String[] read;
try{
RouteReader route = new RouteReader();
read = route.getRoutes();
} catch(IOException iox){
read = new String[1];
read[0] = getResources().getString(R.string.loading_error);
} catch(ArrayIndexOutOfBoundsException aiob){
read = new String[1];
read[0] = getResources().getString(R.string.loading_error);
} catch(NullPointerException npe){
read = new String[1];
read[0] = getResources().getString(R.string.loading_error);
}
return read;
}
#Override
protected void onPostExecute(String[] result) {
values = result;
}
}
This is then called in my onCreate method as new GetRoutes().execute("test");.
However, when I try to run this, my app crashes as a result of a NullPointerException (logcat is available here).
Could you please guide me on how I can fix this?
For further reference, my RouteReader class is as follows:
public class RouteReader extends Reader{
public final static String routeURL =
"http://webservices.nextbus.com/service/publicXMLFeed?command=routeList&a=ttc";
private Map<String, String> routes;
public RouteReader()
throws IOException, ArrayIndexOutOfBoundsException{
super(new URL(routeURL));
routes = xmlToMap();
}
public String[] getRoutes(){
return (String[]) routes.keySet().toArray();
}
public String getRouteNum(String route){
return routes.get(route);
}
private Map<String, String> xmlToMap()
throws IOException, ArrayIndexOutOfBoundsException{
Map<String, String> data = new HashMap<String, String>();
String input;
do{
input = getReader().readLine();
if (input.startsWith("<route")){
String[] read = input.split("\"");
data.put(read[3], read[1]);
}
}while (!input.equals("</body>"));
return data;
}
}
from your log:
Caused by: java.lang.NumberFormatException: Invalid int: "1S"
this is probably caused in this line:
data.put(read[3], Integer.parseInt(read[1]));
We'll your log shows a NumberFormatException at RouteReader lines 35. That's not a NullPointerException - it's a failure to parse a string as an integer, because the string is "1S". You should work out what you want to do with invalid data, and handle it appropriately.
Additionally, you're comparing strings with == instead of equals, which is almost never what you want to do. Personally I wouldn't try to use string operations to parse the XML in the first place: use an XML parser... That's what it's there for. Your current approach is very brittle in the face of seemingly-harmless changes in the XML format.
Related
I developing android app and now I have problem. Below is a part of my code, and it keeps skipping the "for" part. When I put a breakpoint inside for statement, it stops at the point, and executes the lines very well and makes an output that I want. When I just 'run' app, it skips that part so "String locations" value doesn't change. I googled and some say it's thread-related problem. So I put synchroinzed on the method, still not working. Any other suggestions?
UPDATE
I was trying to show code only related to the problem, but I think now showing the whole would be more useful for those who try to help so here's my entire code on showMapActivity. You can see I've tried some ways around and nothing worked. Saving path's information into String url is where I'm having problem. I tested, and other parts seem to work fine. I know my code is really massy, that was why I only posted parts of the code. TMap related classes are imported from .jar file.
public class showMapActivity extends Activity {
TMapData tmapdata=new TMapData();
TMapView tmapView;
TMapPoint origin, dest;
volatile ArrayList<TMapPoint> points=new ArrayList<>();
private TextView x;
private TextView y;
private HashMap<String,LatLng> coordinates;
private HashMap<LatLng,Double> finalpoint;
static private ConcurrentHashMap<Double,Double> path;
#Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_show_map);
coordinates=new HashMap<>();
Intent intent=getIntent();
tmapView=new TMapView(this);
path=new ConcurrentHashMap<>();
coordinates=(HashMap<String,LatLng>)intent.getSerializableExtra("coordinate");
path=getPathPoints(coordinates);
int i=0;
String url=getUrl();
//String url = "https://maps.googleapis.com/maps/api/elevation/json?locations=";
//String locations="";
/*
Iterator<Double> keys= path.keySet().iterator();
while(keys.hasNext()){
Double key=keys.next();
//String lat=String.valueOf(key);
//String lng=String.valueOf(path.get(key));
locations=locations+String.valueOf(key)+","+String.valueOf(path.get(key));
if(keys.hasNext())
locations=locations+"|";
}path.entrySet()
*/
/*
for(ConcurrentHashMap.Entry<Double,Double> elem : path.entrySet())
{
String lat=String.valueOf(elem.getKey());
String lng=String.valueOf(elem.getValue());
locations=locations+lat+","+lng;
i++;
if(i!=path.size())
{
locations=locations+"|";
}
}
*/
//url=url+locations+"&key=AIzaSyDD88VFMPIfC5sr0XsFL0PDCE-QRN8gQto";
//String url=getUrl(path);
FetchUrl fetchUrl=new FetchUrl();
fetchUrl.execute(url);
}
private ConcurrentHashMap<Double,Double> getPathPoints(HashMap<String,LatLng> coordinates)
{
final ConcurrentHashMap<Double,Double> Path=new ConcurrentHashMap<>();
tmapView.setSKPMapApiKey("6bb5b7f3-1274-3c5e-ba93-790aee876673");
origin=new TMapPoint(coordinates.get("origin").latitude,coordinates.get("origin").longitude);
dest=new TMapPoint(coordinates.get("dest").latitude,coordinates.get("dest").longitude);
tmapdata.findPathData(origin, dest, new TMapData.FindPathDataListenerCallback() {
#Override
public void onFindPathData(TMapPolyLine polyLine) {
points=polyLine.getLinePoint();
for(TMapPoint point : points )
Path.put(point.getLatitude(),point.getLongitude());
}
});
return Path;
}
//ConcurrentHashMap<Double,Double> path
private synchronized String getUrl() {
int i=0;
String url = "https://maps.googleapis.com/maps/api/elevation/json?locations=";
String locations="";
for(HashMap.Entry<Double,Double> elem : path.entrySet())
{
String lat=String.valueOf(elem.getKey());
String lng=String.valueOf(elem.getValue());
locations=locations+lat+","+lng;
i++;
if(i!=path.size())
{
locations=locations+"|";
}
}
url=url+locations+"&key=AIzaSyDD88VFMPIfC5sr0XsFL0PDCE-QRN8gQto";
//https://maps.googleapis.com/maps/api/elevation/json?locations=
// 39.7391536,-104.9847034|36.455556,-116.866667&key=AIzaSyDD88VFMPIfC5sr0XsFL0PDCE-QRN8gQto
// Output format
return url;
}
private class FetchUrl extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... url) {
// For storing data from web service
String data = "";
try {
// Fetching the data from web service
//downloadURL
data = downloadUrl(url[0]);
Log.d("Background Task data", data.toString());
} catch (Exception e) {
Log.d("Background Task", e.toString());
}
return data;
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
//ParserTask
ParserTask parserTask = new ParserTask();
// Invokes the thread for parsing the JSON data
parserTask.execute(result);
}
}
private String downloadUrl(String strUrl) throws IOException {
String data = "";
InputStream iStream = null;
HttpURLConnection urlConnection = null;
try {
URL url = new URL(strUrl);
// Creating an http connection to communicate with url
urlConnection = (HttpURLConnection) url.openConnection();
// Connecting to url
urlConnection.connect();
//읽은 데이터를 버퍼에 저장
// Reading data from url
iStream = urlConnection.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(iStream));
StringBuffer sb = new StringBuffer();
String line = "";
while ((line = br.readLine()) != null) {
sb.append(line);
}
data = sb.toString();
Log.d("downloadUrl", data.toString());
br.close();
} catch (Exception e) {
Log.d("Exception", e.toString());
} finally {
iStream.close();
urlConnection.disconnect();
}
return data;
}
private class ParserTask extends AsyncTask<String, Integer, ArrayList<Double>> {
// Parsing the data in non-ui thread
#Override
protected ArrayList<Double> doInBackground(String... jsonData) {
JSONObject jObject;
ArrayList<Double> altitude = null;
try {
jObject = new JSONObject(jsonData[0]);
Log.d("ParserTask",jsonData[0].toString());
//DataParser class 호출
DataParser parser = new DataParser();
Log.d("ParserTask", parser.toString());
// Starts parsing data
altitude = parser.parse(jObject);
Log.d("ParserTask","Getting Altitudes");
Log.d("ParserTask",altitude.toString());
} catch (Exception e) {
Log.d("ParserTask",e.toString());
e.printStackTrace();
}
return altitude;
}
// Executes in UI thread, after the parsing process
#Override
protected void onPostExecute(ArrayList<Double> result) {
finalpoint=new HashMap<>();
LatLng latLng;
int i=0;
for(HashMap.Entry<Double,Double> elem : path.entrySet() )
{
latLng=new LatLng(elem.getKey(),elem.getValue());
finalpoint.put(latLng,result.get(i++));
}
x = (TextView) findViewById(R.id.textView5);
y = (TextView) findViewById(R.id.textView6);
x.setText(String.valueOf(finalpoint.get(coordinates.get("origin"))));
y.setText(String.valueOf(finalpoint.get(coordinates.get("dest"))));
}
}
}
(Apologies for posting this as an answer - I don't yet have the required reputation to comment)
Simply adding synchronized to a method doesn't necessarily guarantee thread safety.
How and when is path being populated?
Update after additional information provided
The problem seems to be that the path points are being generated asynchronously, and you are trying to use them before the generation process has finished (or perhaps even started). This happens because the findPathData simply starts the generation process and returns immediately (i.e. before the generation process has finished). In your code, you then go on and build the URL which is supposed to contain the point data immediately. At this point the background point generation process may not have finished, and may not have even started. As a result the point map may be empty or incomplete, and your URL will not be generated as you expect.
You need to find a way to wait until all of the path point data has been returned by the asynchronous processing before creating the URL. This looks like it could be very difficult, if not impossible, with the version of the findPathData method you are using, because it returns points via the callback one at a time and you may not know how many will be generated.
I had a quick look at the API for TMapData and it has a findPathDataAll method which seems to generate all the points and return them in a single callback call rather than one by one. If this is indeed what it does (sorry, I can't read Korean), you could use this method and then generate the URL from the callback, because when it's called you know that the generation process has been completed. If you do this, be careful to make sure that you're on the main thread before interacting with the UI or Activity.
Hope that helps.
I have some methods below and basically my issue is when I try to read from a .txt file. The application works fine in memory. When I add the decode() to the overloaded constructor I get the following error in Tomcat Server logs:
Constructor threw exception; nested exception is java.lang.NumberFormatException: For input string: ""
When I look more at this it points to this line in the decode method
currentDVD.setDvdId(Integer.parseInt((currentTokens[0]))); but I can't seem to figure out what the issue is. At one point I was able to read from the file and then I was trying to get the encode method to work and something happened at some point. Any help would be appreciated.
public class DvdLibraryInFileImpl implements DvdLibraryDao {
private Map<Integer, DVD> dvdMap = new HashMap<>();
public static final String DVD_FILE = "dvd.txt";
public static final String DELIMITER = "::";
private static int dvdIdCounter = 0;
public DvdLibraryInFileImpl() throws FileNotFoundException {
decode();
}
#Override
public DVD addDVD(DVD dvd) {
dvd.setDvdId(dvdIdCounter);
dvdIdCounter++;
dvdMap.put(dvd.getDvdId(), dvd);
return dvd;
}
#Override
public DVD getDVDById(int dvdId) {
return dvdMap.get(dvdId);
}
#Override
public List<DVD> getAllDVDSByName(String searchByName) {
throw new UnsupportedOperationException("Not supported yet.");
}
#Override
public List<DVD> getAllDVDS() {
List<DVD> allDVDS = new ArrayList<>(dvdMap.values());
return allDVDS;
}
#Override
public void updateDVD(DVD dvd) {
dvdMap.put(dvd.getDvdId(), dvd);
}
#Override
public void removeDVD(int dvdId) {
dvdMap.remove(dvdId);
}
#Override
public void decode() throws FileNotFoundException {
Scanner sc = new Scanner(new BufferedReader(new FileReader(DVD_FILE)));
String[] currentTokens;
while (sc.hasNextLine()) {
String currentLine = sc.nextLine();
currentTokens = currentLine.split(DELIMITER);
DVD currentDVD = new DVD();
currentDVD.setDvdId(Integer.parseInt((currentTokens[0])));
currentDVD.setTitle(currentTokens[1]);
currentDVD.setReleaseDate(currentTokens[2]);
currentDVD.setMpaaRating(currentTokens[3]);
currentDVD.setDirectorsName(currentTokens[4]);
currentDVD.setStudio(currentTokens[5]);
currentDVD.setUserRating(currentTokens[6]);
dvdMap.put(Integer.parseInt((currentTokens[0])), currentDVD);
}
}
#Override
public void encode() throws IOException {
PrintWriter out = new PrintWriter(new FileWriter(DVD_FILE));
Set<Integer> keySet = dvdMap.keySet();
for (Integer i : keySet) {
out.print((dvdMap.get(i)).getDvdId());
out.print(DELIMITER);
out.print((dvdMap.get(i)).getTitle());
out.print(DELIMITER);
out.print((dvdMap.get(i)).getReleaseDate());
out.print(DELIMITER);
out.print((dvdMap.get(i)).getMpaaRating());
out.print(DELIMITER);
out.print((dvdMap.get(i)).getDirectorsName());
out.print(DELIMITER);
out.print((dvdMap.get(i)).getStudio());
out.print(DELIMITER);
out.print((dvdMap.get(i)).getUserRating());
out.println("");
}
out.flush();
out.close();
}
}
The problem is because currentLine does NOT have the proper input data received (from user entry).
You should ensure that currentTokens is having proper DvdId set (should be numeric value), otherwise Integer.parseInt((currentTokens[0])) line will throw NumberFormatException for non-numeric or empty ("") data.
You need to ensure that the input data entered is correct with numeric dvdid.
For example, enter below data:
1234::DVDTITLE::29-OCT-2016::RATING1::DIRECTOR::STUDIO::RATING2
I strongly recommend you to add the input validations to handle the scenarios like entering the non-numeric values or rating higher that max value or director name containg numeric values, etc....
The problem is this adapter is giving the error although i have pass the Object array to it.(Read the methods belows then you will find what i want to know from you guys)
This method declares a List of private class objects. Then return that list of object to onPostExecute method.
private class DownloadXmlTask extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... urls) {
try {
return loadXmlFromNetwork(urls[0]);
} catch (IOException e) {
return "I/O exception ae hy";
} catch (XmlPullParserException e) {
return "XML pull parser ke exception ae hy";
}
}
#Override
protected void onPostExecute(List<StackOverflowXmlParser.Entry> result) {
//Log.d(TAG,result.toString());
ArrayAdapter<String> adapter;
adapter = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,result);
setListAdapter(adapter);
}
private Object loadXmlFromNetwork(String urlString) throws XmlPullParserException, IOException {
InputStream stream = null;
// Instantiate the parser
StackOverflowXmlParser stackOverflowXmlParser = new StackOverflowXmlParser();
List<StackOverflowXmlParser.Entry> entries = null;
String title = null;
String url = null;
String summary = null;
try {
stream = downloadUrl(urlString);
entries = stackOverflowXmlParser.parse(stream);
} finally {
if (stream != null) {
stream.close();
}
}
for (StackOverflowXmlParser.Entry entry : entries)
{
Log.d(TAG, entry.link + " /" + entry.title);
}
return entries;
}
I think it should be onPostExecute(List<StackOverflowXmlParser.Entry> result)
And you AsyncTask should be
extends AsyncTask<smth, smth, List<StackOverflowXmlParser.Entry> >
ArrayAdapter<String> requires that you provide it a String[] or a List<String>. You are trying to pass in Object[], which is neither String[] nor List<String>. And, it would appear that you are really trying to populate the ListView with a list of StackOverflowXmlParser.Entry objects, which are not String objects.
My guess is that the right answer is for you to create an ArrayAdapter<StackOverflowXmlParser.Entry> instead of an ArrayAdapter<String>.
Regardless, you need to ensure that the data type in your declaration (String in ArrayAdapter<String>) matches the data type in your constructor parameter that supplies the data to be adapted.
I have a LinkedHashMap which fills with data from db with loop "for" string by string and when I try to show the first or the last String, the method can show me only the last String in log. But in application listViewContent is filled fully. So I don't understand why I can't see any string that I want. I need to collect all strings I get from db and compare them in future.
How can I collect all strings and what method should I call to show the string I want to see?Unfortunately I can only retrieve one (and the last instead of the first) string.
Here is my example code :
protected void onCreate(Bundle saveInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
FirstMethod();
}
public FirstMethod() {
SecondMethod newMethod = .. // getting data from the second method
}
public SecondMethod() {
public void onResponseReceived(String result) {
try {
...
if (posts != null) {
for (WallPostItem post : posts) { // this loop
//create new map for a post
Map<String, Object> map = new LinkedHashMap<String, Object>();
map.put(ATTRIBUTE_NAME_TEXT, post.text);
PictureItem postPicture = new PictureItem();
map.put(ATTRIBUTE_NAME_IMAGE, postPicture);
map.put(ATTRIBUTE_NAME_DATE, post.date);
sAdapter.notifyDataSetChanged();
};
};
...
List<Map.Entry<String, Object>> list = new ArrayList<Map.Entry<String, Object>>(GlobalMap.entrySet());
Map.Entry<String, Object> firstInsertedEntry = list.get(0);
Log.w("FirstEntryOfMap",""+firstInsertedEntry); // this log shows me the last string instead of the first
}
if (isRefresh) {
isRefresh = false;
lvSimple.setSelectionAfterHeaderView();
}
} catch (Exception e) {
Log.d("exceptions", "problem in get wall post task after post execute: " + e.toString());
}
}
You aren't putting your values into a List, you are putting them into a Map (that preserves key order). I would suggest you create a POJO class,
class MyAttribute {
final String postName;
final PictureItem postPicture;
final Date postDate;
public MyAttribute(String postName, PictureItem postPicture, Date postDate) {
this.postName = postName;
this.postPicture = postPicture;
this.postDate = postDate;
}
public String getPostName() {
return postName;
}
public Date getPostDate() {
return postDate;
}
public PictureItem getPostPicture() {
return postPicture;
}
}
Then you could create a
List<MyAttribute> myAttributes = new ArrayList<>();
In my application I need to convert my arraylist to a string of an array. However, I am getting an error:
ClassCastException: java.lang.Object[] cannot be cast to java.lang.String[] android
At the line with listofurls I am getting the error: listofurls = (String[])image_urls.toArray();
This is the full code:
public class Test2 extends AsyncTask<Void, Void, Void>
{
String[] listofurls ;
private static final String url = "http://www.tts.com/album_pro/array_to_encode";
JSONParser jParser = new JSONParser();
ArrayList<String> image_urls = new ArrayList<String>();
protected void onPreExecute() {
//Log.e(LOG_CLASS, "in side assyntask");
}
protected Void doInBackground(Void... voids) {
Log.v("Async","Async");
JSONObject json = jParser.getJSONFromUrl(url);
try {
JSONObject seo = json.getJSONObject("SEO");
JSONArray folio = seo.getJSONArray("Folio");
// JSONArray image_urls1 = new JSONArray();
//String s1=seo.getString("Folio");
for(int i=0;i<folio.length();++i) {
String m = folio.getString(i);
Log.v("M"+i,m);
image_urls.add(m);
Log("test-url"+image_urls);
}
} catch(Exception e) {
e.printStackTrace();
}
listofurls = (String[])image_urls.toArray(); //ERROR OCCURS HERE
return null;
}
private void Log(String string) {
Log.v("Test",string);
}
protected void onProgressUpdate(Integer... progress) { }
protected void onPostExecute(Void result) {
mAdapter = new ImagePagerAdapter(getSupportFragmentManager(),listofurls.length );
mAdapter.setImageurls(listofurls);
mPager.setAdapter(mAdapter);
}
try
listofurls = image_urls.toArray(new String[image_urls.size()]);
Note: I suggest to rename listofurls to arrayOfURLs
You should use toArray as mentioned above, but not in that way.
Either initialize the array first and fill it:
String[] urlArray = new String[image_urls.size()];
image_urls.toArray(urlArray);
After which, urlArray will contain all the Strings from image_urls, or pass in a zero-length String array:
listofurls = (String[]) image_urls.toArray(new String[0]);
See the documentation for toArray().
You just need to get the contents of arraylist in an array, right??
Can't u do like this?
for(int i=0;i<folio.length();++i)
{
String m = folio.getString(i);
Log.v("M"+i,m);
image_urls.add(m);
Log("test-url"+image_urls);
listofurls[i] = m ;
}
listofurls = image_urls.toArray(new String[0]);
that should do the trick for all cases, even if you don't know the size of the resulting array.
Try this:
ArrayList<String> stock_list = new ArrayList<String>();
stock_list.add("stock1");
stock_list.add("stock2");
String[] stockArr = new String[stock_list.size()];
stockArr = stock_list.toArray(stockArr);
for(String s : stockArr)
System.out.println(s);
Taken directly from here: link