2010-09-29 33 views
2

我是新開發Android應用程序,在學校只有一點Java經驗。當我正在查找Android Beginners組時,我被重定向到Google組頁面的StackOverflow。我有一個問題,關於什麼是從Web源中獲取內容並解析它的最佳實踐。首先,我最終希望讓我的應用程序通過線程(通過使用Handler?),但是,現在我的問題是,我創建的(服務器)類連接和獲取內容通常無法檢索內容,這會導致我的JSON解析器類(JSONParser)失敗,並且我的視圖不顯示任何內容。在導航到前一個Activity後,嘗試在同一個遠程URI上調用connect(),fetch()和parse()方法後,它就可以工作。從網絡提取內容的最佳做法?

這是爲什麼(有時檢索遠程數據)發生有時,但並非總是如此?最佳做法是什麼,包括使用ProgressDialog和內部Handler類,以使我的應用程序與用戶無縫連接。這是問這個問題的最佳地點嗎?感謝您的幫助

這是我現在使用的代碼。

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(); 
} 

} 

,並從我的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()); 

} 

下面的代碼是我的JSON解析器類

/** 
* 
*/ 

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("\\<.*?>", ""); 
    } 

} 

回答

0

好,沒有任何錯誤日誌很難猜測可能是你的問題。

但是,如果您使用AndroidHttpClient進行連接,則運氣可能會更好。它在恢復/重試方面做得很好。

此外,關於多線程,AsyncTask是一個很好的起點。

我的ShortcutLink應用程序可在github上找到,可能會讓您瞭解如何使用這些應用程序。

編輯

您可以嘗試使用下面的代碼從java2s.com直接轉換的InputStream一個String。

HttpClient client = new DefaultHttpClient(); 
HttpGet get = new HttpGet("http://stackoverflow.com/a/b/c?param=value"); 
String content = client.execute(get, new BasicResponseHandler()); 

你可以再喂content

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(); 
} 
+0

感謝您的輸入反應。我在Eclipse和Android DDMS中使用過,問題是「new String(baf.toByteArray())」返回的字符串是空的。這幾乎就像數據檢索失敗一樣,所以方法放棄了,並返回一個空字符串,這導致我的解析器失敗並且我的視圖沒有被填充。 AndroidHttpClient聽起來很有希望。我會嘗試這一點,並希望獲得更好的結果。 – 2010-09-30 01:20:42

+0

其他警告/錯誤怎麼樣?它們是否在logcat中顯示? – bhups 2010-09-30 04:06:11

+0

在我的設備上運行時,我會看到與WifiService(acquireWifiLockedLock)和LocationMasfClient(getNetworkLocation)相關的藍色系統消息,我不認爲這與錯誤有任何關係。 我無法一致地重現錯誤。在設備上測試期間,錯誤(空字符串)似乎在更長時間的網絡活動(第一次啓動應用程序,或幾分鐘不使用等)之後發生,並且在高活動期間發生_less_ (儘可能快地測試/啓動設備上的查詢)。 – 2010-09-30 04:23:50

0

你可能會使用Apache HTTP模塊(包含在Android設備),它可以讓你的HTTP的響應得到3行有一個更容易的時間直接進入JSON解析器:

JSONObject json = new JSONObject(content); 

一般情況下,你可以換了此操作的AsyncTask內,顯示/從其隱藏ProgressDialog分別爲和onPostExecute方法,最後調用Handler將控制權返回給您的活動。

0

對於這樣的任務,我會說你一定要去AsyncTask

使用這個,你可以實現doInBackground()函數來下載和解析你的數據。

至於進度,獲取簡單文本頁面的狀態(例如您的示例)並不那麼容易。我會建議分別在onPreExecute()onPostExecute()函數中分別顯示和隱藏一個「繁忙」圖像。

此外,當所有說出和完成時您要顯示的任何內容都可以在onPostExecute()中執行。這將從主UI線程中調用,因此直接修改您已顯示並想要更新的View將是安全的。

爲了獲得你正在尋找的大部分我會做下面的事情。

View v = ... ; // the view you want to update with stuff 

new AsyncTask<String, Void, String>() 
{ 
    @Override 
    protected Void doInBackground(String... urls) 
    { 
     String ret = "" ; // Doesn't have to be a String 

     try{ 
      JSONObject jo = new JSONObject(SimpleHttpClient.getPage(urls[0])) ; 

      // parse everything you want into ret 
      ret = jo.getString("xxx") ; 
     } 
     catch(Exception e){ 
      ret = "Error: Could not get band data: "+e ; 
     } 

     return ret ; 
    } 

    public void onPreExecute() 
    { 
     // display some busy spinny thing 
    } 

    public void onPostExecute(String s) 
    { 
     // hide some busy spinny thing 

     // do what you want to the view! 
     v.setText(s) ; 
    } 
}.execute("Your url here") ; 

對於JSONObject jo = new JSONObject(SimpleHttpClient.getPage(url));線, 我要說@ Dave的答案肯定涵蓋了並且可能是最容易/更corrent(/也許更有效)的解決方案。

作爲一種替代方法,下面的類提供了一個非常簡單的接口(當確定忽略時捕獲異常),同時讓用戶能夠在需要時進行更深入的挖掘(如果適用,可獲得InputStream或Reader)。

SimpleHttpClient.java:

import java.io.BufferedReader; 
import java.io.IOException; 
import java.io.InputStream; 
import java.io.InputStreamReader; 

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

public class SimpleHttpClient 
{ 
    private static final int BUFSIZE = 2048 ; 

    /** 
    * Returns an HttpEntity of the page specified by the given URL. 
    * 
    * @param url URL of the page to retrieve. 
    * @return HttpEntity representing the text of the page. 
    * @throws ClientProtocolException 
    * @throws IOException 
    */ 
    private static HttpEntity getEntity(String url) throws ClientProtocolException, IOException 
    { 
     HttpClient client = new DefaultHttpClient() ; 
     HttpUriRequest request = new HttpGet(url) ; 

     HttpResponse response = client.execute(request) ; 

     return response.getEntity() ; 
    } 

    /** 
    * Send request to given URL and disregard response. 
    * 
    * @param url URL of the page to request. 
    * @throws ClientProtocolException 
    * @throws IOException 
    */ 
    public static void send(String url) throws ClientProtocolException, IOException 
    { 
     getEntity(url) ; 
    } 

    /** 
    * Send request to given URL and disregard response. 
    * 
    * @param url URL of the page to request. 
    * @return Return true if no exceptions, false if exceptions occurred. 
    */ 
    public static boolean trySend(String url) 
    { 
     boolean ret = true ; 
     try { 
      send(url) ; 
     } 
     catch (Exception e) { ret = false ; } 
     return ret ; 
    } 

    /** 
    * Returns an InputStream of the page specified by the given URL. 
    * 
    * @param url URL of the page to retrieve. 
    * @return InputStream representing the text of the page. 
    * @throws ClientProtocolException 
    * @throws IOException 
    */ 
    public static InputStream getStream(String url) throws ClientProtocolException, IOException 
    { 
     return getEntity(url).getContent() ; 
    } 

    /** 
    * Returns an BufferedReader of the page specified by the given URL. 
    * 
    * @param url URL of the page to retrieve. 
    * @return BufferedReader representing the text of the page. 
    * @throws ClientProtocolException 
    * @throws IOException 
    */ 
    public static BufferedReader getReader(String url) throws ClientProtocolException, IOException 
    { 
     HttpEntity he = getEntity(url) ; 

     int size = (int)he.getContentLength() ; 
     size = (BUFSIZE > size && size > 0) ? size+32 : BUFSIZE ; 
     return new BufferedReader(new InputStreamReader(he.getContent()), size) ; 
    } 

    /** 
    * Return the page specified by the given URL as a String. 
    * 
    * @param url URL of the page to retrieve. 
    * @return String representing the text of the page. 
    * @throws ClientProtocolException 
    * @throws IOException 
    */ 
    public static String getPage(String url) throws ClientProtocolException, IOException 
    { 
     StringBuffer sb = new StringBuffer("") ; 
     String line = "" ; 
     String nl = System.getProperty("line.separator") ; 
     String page = "" ; 

     BufferedReader in = null; 
     try { 

      in = getReader(url) ; 

      while((line = in.readLine()) != null) { 
       sb.append(line + nl) ; 
      } 

      page = sb.toString() ; 
     } 
     finally { 
      if(in != null) try { in.close() ; } catch (Exception e) {} ; 
     } 

     return page ; 
    } 

    /** 
    * Same as getPage, returns the given URL as a String, but masks any Exceptions and 
    * returns null instead. 
    * 
    * @param url URL of the page to retrieve. 
    * @return String representing the text of the page, or null if there is an error. 
    */ 
    public static String tryPage(String url) 
    { 
     String tmp = null ; 
     try { 
      tmp = getPage(url) ; 
     } 
     catch (Exception e) {} ; 
     return tmp ; 
    } 
}