tags:

views:

108

answers:

1

I'm new to developing Android applications, and have only a little experience with Java in school. I was redirected to StackOverflow from the Google groups page when I was looking for the Android Beginners group. I have a question about what is best practice to pull content from a web source and parse it.

Firstly, I would eventually like to have my application threaded (by use of Handler?), however, my issue now is that the class I have created (Server) to connect and fetch content often fails to retrieve the content, which causes my JSON parser class (JSONParser) to fail, and my View to display nothing. After navigating to the previous Activity, and attempting to call the connect(), fetch(), and parse() methods on the same remote URI, it will work.

Why does this (sometimes retrieve the remote data) happen sometimes, but not always? What is the best practice, including the use of ProgressDialog and the internal Handler class, to make my application seemless to the user. Is this the best place to ask this question? Thanks for your help

Here is the code I'm using now.

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;

import org.apache.http.util.ByteArrayBuffer;

import android.util.Log;

public class Server {

 public static final String HTTP_PROTOCOL = "https://";
 public static final String EXAMPLE_NET_DOMAIN = "example.domain.net/";
 private final String API_KEY = "1234567890";
 private static final String API_ENDPOINT = "api.js?";
 public final String FORMAT = "json";

 public String API_VERSION;
 public String METHOD;
 public String OPTIONAL_ARGUMENTS;

 public String json;
 public URL jURL;
 public URLConnection jConnection;
 public BufferedReader jIn;
    public InputStream is = null;



 /**
  * @param aPIVERSION
  * @param mETHOD
  * @param oPTIONALARGUMENTS
  */
 public Server(String aPIVERSION, String mETHOD, String oPTIONALARGUMENTS) {
  super();
  API_VERSION = aPIVERSION;
  METHOD = mETHOD;
  OPTIONAL_ARGUMENTS = oPTIONALARGUMENTS;

  connect();
  Log.i("DEBUG:","connect();");
 }

 /**
  * @param aPIVERSION
  * @param mETHOD
  */
 public Server(String aPIVERSION, String mETHOD) {
  super();
  API_VERSION = aPIVERSION;
  METHOD = mETHOD;
  OPTIONAL_ARGUMENTS = "";

  connect();
 }

 /**
  * @param aPIVERSION
  * @param mETHOD
  */
 public void connect(){

  try {
         jURL = new URL(HTTP_PROTOCOL 
           + EXAMPLE_NET_DOMAIN 
           + API_ENDPOINT 
           + "api=" + this.API_VERSION 
           + "&method=" + this.METHOD
           + "&format=" + FORMAT
           + "&apikey=" + API_KEY
           + this.OPTIONAL_ARGUMENTS);

         jConnection = jURL.openConnection();

        } catch (IOException e) {

         Log.e("USER: ", "Error in server connection.");
         Log.e("DEBUG:", "Error in server connection");
  }
        Log.i("USER: ", "Connection success!");
 }


 public String fetch() {

  try {
   is = jConnection.getInputStream();
  } catch (IOException e) {
   // TODO Auto-generated catch block
   Log.e("DEBUG:", "fetch-1() error");
  }
      BufferedInputStream bis = new BufferedInputStream(is);
      ByteArrayBuffer baf = new ByteArrayBuffer(50);

     int current = 0;
     try {
   while((current = bis.read()) != -1){
       baf.append((byte)current);
   }
   Log.i("DEBUG:",new String(baf.toByteArray()));

  } catch (IOException e) {
   // TODO Auto-generated catch block
   Log.e("DEBUG:","fetch() ERROR!");
  }

     /* Convert the Bytes read to a String. */
  Log.i("DEBUG:",new String(baf.toByteArray()));
     return new String(baf.toByteArray());

 }

 /* Returns a string containing a concise, human-readable description of jURL
  * 
  */
 public String showUrl() {
  return jURL.toExternalForm();
 }

}

and the code from my ListActivity

/* Called when the activity is starting. This is where most initialization should go: calling setContentView(int) to inflate the activity's UI, using findViewById(int) to programmatically interact with widgets in the UI, calling managedQuery(android.net.Uri, String[], String, String[], String) to retrieve cursors for data being displayed, etc. 
  * @see android.app.Activity#onCreate()
  */
 @Override
 public void onCreate(Bundle savedInstanceState){
  super.onCreate(savedInstanceState);
  Log.i("LIFECYCLE: ", "RS.class onCreate()");

  Server serverConnection = new Server(API_VERSION, METHOD, OPTIONAL_ARGUMENTS);

     json = serverConnection.fetch();

     JSONParser jParser = new JSONParser(json);

     groupData = jParser.parseJsonForRecentShowList();

     SimpleAdapter adapter = new SimpleAdapter( this, groupData, android.R.layout.simple_list_item_2, new String[] { "venue","showdate"},
                new int[]{ android.R.id.text2, android.R.id.text1 } );
     setListAdapter( adapter );

  registerForContextMenu(getListView());

 }

Here is my JSON Parser class

/**
 * 
 */

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.util.Log;


/**
 * @author 
 *
 */
public class JSONParser {

    public List<Map<String, String>> groupData = new ArrayList<Map<String, String>>();

 private String jString;
 private Map<String, String> group;
 private JSONArray jArray;
 private String setlistData;
 public String notesString = "";

 public String[] splitSetsArray;
 public String[] splitEncoreArray;
 public String[] extraWork;
 public String[] notesArray;

 public String pVenue_text;
 public String pCity_text;
 public String pState_text;
 public String pCountry_text;
 public String pDate_text;

 public String pSet1_text;
 public String pSet2_text;
 public String pSet3_text;
 public String pEncore1_text;
 public String pEncore2_text;
 public String pNotes_text;

 public int totalNumberOfSets;
 public int totalNumberOfEncores;
 public int totalNumberOfNotes;

 public JSONObject jObject;


 public JSONParser(String json) {
  // TODO Auto-generated constructor stub
  jString = json;
 }

 /**
  * @return
  */
 public List<Map<String, String>> parseJsonForRecentShowList(){

  try {
   jArray = new JSONArray(jString);

   for(int i=0;i<jArray.length();i++) {
    jObject = jArray.getJSONObject(i);

    group = new HashMap<String, String>();
    group.put("showdate", jObject.getString("showdate"));
    group.put("venue", jObject.getString("venue"));
    groupData.add(group);
   }
  } catch (JSONException e) {
   Log.e("DEBUG: ", "JSON Parse error!");
  }
  return groupData;
 }

 /**
  * 
  */
 public void parseJsonForSetlistData(){
  if(jString != null){
      try {
    jArray = new JSONArray(jString);

    jObject = jArray.getJSONObject(0);

    pVenue_text  = jObject.getString("venue")  ;
    pCity_text   = jObject.getString("city") + ", " ;
    pState_text  = jObject.getString("state") + ", " ;
    pCountry_text  = jObject.getString("country") ;
    pDate_text   = jObject.getString("nicedate")  ;

    setlistData = nohtml(jObject.getString("setlistdata"));

    splitSetsArray = setlistData.split("Set..:.");
       totalNumberOfSets = splitSetsArray.length-1;

       String[] splitEncoreArray = splitSetsArray[splitSetsArray.length-1].split("Encore:.");
       totalNumberOfEncores = splitEncoreArray.length-1;
       splitSetsArray[splitSetsArray.length-1] = splitEncoreArray[0];
       splitEncoreArray[0] = "";

       extraWork = splitEncoreArray[splitEncoreArray.length-1].split("\\[1\\]");

    notesArray = extraWork[extraWork.length-1].split("\\[.\\]");
    totalNumberOfNotes = notesArray.length-1;
    splitEncoreArray[splitEncoreArray.length-1] = extraWork[0];
    //notesArray[0] = "";

    for(int i=0;i<notesArray.length;i++){
     int number = i+1;
     notesString += "["+number+"] "+notesArray[i] + "\n";
    }

    if(totalNumberOfSets != 0) {
     pSet1_text = "Set 1: " + splitSetsArray[1];
     if (totalNumberOfSets > 1){
      pSet2_text = "Set 2: " + splitSetsArray[2];
     } else {
      pSet2_text = "";
     }
     if (totalNumberOfSets > 2){
      pSet3_text = "Set 3: " + splitSetsArray[3];
     } else {
      pSet3_text = "";
     }
    }

    pEncore1_text = "Encore: " + splitEncoreArray[1];
    if (totalNumberOfEncores > 1) {
     pEncore2_text = "Encore 2: " + splitEncoreArray[2];
    } else {
     pEncore2_text = "";
    }
    pNotes_text = notesString;

    Log.e("DEBUG: ", "JSON Parsed!");

   } catch (JSONException e) {

    Log.e("ERROR:","caught JSON Exception at parseForSetlistData()");

   }

  } else {

   Log.e("ERROR:", "jString = null");
   pVenue_text = "I'm Sorry, the Setlist Data could not be retrieved from server. Please try again.";

  }

    }

    public void parseJsonForReviews(){
  try {
   jArray = new JSONArray(jString);

   for(int i=0;i<jArray.length();i++){
    jObject = jArray.getJSONObject(i);

    group = new HashMap<String, String>();
    group.put("author", jObject.getString("author"));
    group.put("review", jObject.getString("review"));
    groupData.add(group);
    }
  } catch (JSONException e) {
   Log.e("DEBUG: ", "JSON Reviews parse error!");
  }

    }

    public List<Map<String, String>> parseJsonForYears(){
  try {
   jArray = new JSONArray(jString);

   for(int i=0;i<jArray.length();i++){
    JSONObject jObject = jArray.getJSONObject(i);
    group = new HashMap<String, String>();
    group.put("showdate", jObject.getString("showdate"));
    group.put("venue", jObject.getString("venue"));
    groupData.add(group);
   }
  } catch (JSONException e) {
   Log.e("DEBUG: ", "JSON Years parse error!");
  }

  Collections.reverse(groupData);

  return groupData;
    }

    public String nohtml(String json){
     return json.toString().replaceAll("\\<.*?>", "");
    }

}
A: 

Well, without any error logs it's difficult to guess what might be your issue.

However, you might have better luck if you used the AndroidHttpClient for your connection. It does a good job of recovering/retrying.

Also, regarding multi-threading, AsyncTask is a good starting place.

My ShortcutLink application, available on github, might give you a heads up on how to use these.

EDIT

You might try converting the InputStream directly to a String using the following code from java2s.com.

public static String convertStreamToString(InputStream is) throws Exception {
  BufferedReader reader = new BufferedReader(new InputStreamReader(is));
  StringBuilder sb = new StringBuilder();
  String line = null;
  while ((line = reader.readLine()) != null) {
    sb.append(line + "\n");
  }
  is.close();
  return sb.toString();
}
aprock
Thanks for your respone. I've used in Eclipse and Android DDMS, and the issue is that the string returned by "new String(baf.toByteArray())" is empty. It's almost as if the data retrieval FAILS, so the method gives up, and returns an empty string, which leads to my Parser failing and my View not being populated.AndroidHttpClient sounds promising. I will try this and hope for better results.
corrinado_swam
how about other warnings/errors? Do they show up too in logcat?
bhups
Running on my device, I will see blue-colored system messages related to WifiService (acquireWifiLockedLock) and LocationMasfClient (getNetworkLocation), I don't think this has anything to do with the error. I cannot reproduce the error consistently. During on-device testing, the error (empty string) seems to happen _more_ after longer periods of network in-activity (first time starting the app, or several minutes of not using, etc), and happen _less_ in periods of high-activity (testing/initiating queries on-device as fast as possible).
corrinado_swam
the source is now located at http://github.com/emdog4/TwoPhish. I will hopefully have time tomorrow to instate AndroidHTTPClient. (note: if you download this source, years 2005-2008 have no data because of a hiatus for the band, so note this is not an error)
corrinado_swam