2010-09-29 35 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 ; 
    } 
}