从网络上提取内容的最佳做法?

时间:2010-09-29 20:51:44

标签: android

我是开发Android应用程序的新手,在学校里只有一点Java经验。当我在寻找Android初学者组时,我被重定向到Google群组页面的StackOverflow。我有一个问题是什么是从Web源提取内容并解析它的最佳做法。

首先,我最终希望将我的应用程序线程化(使用Handler?),但是,我现在的问题是我创建的类(Server)连接和获取内容经常无法检索内容,导致我的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 Parser类

/**
 * 
 */

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

}

3 个答案:

答案 0 :(得分:0)

好吧,没有任何错误日志,很难猜出你的问题可能是什么。

但是,如果您使用AndroidHttpClient进行连接,可能会有更好的运气。它在恢复/重试方面做得很好。

此外,关于多线程,AsyncTask是一个很好的起点。

我在github上提供的ShortcutLink应用程序可能会让您了解如何使用这些应用程序。

修改

您可以尝试使用java2s.com中的以下代码将InputStream直接转换为String。

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

答案 1 :(得分:0)

您可以更轻松地使用Apache HTTP模块(包含在Android中),它可以在3行中获得HTTP GET的响应:

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

然后,您可以将content直接提供给JSON解析器:

JSONObject json = new JSONObject(content);

通常,您可以将此操作包装在AsyncTask内,分别从其onPreExecuteonPostExecute方法显示/隐藏ProgressDialog,最后调用Handler将控制权返回给您完成后的活动。

答案 2 :(得分: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的答案肯定涵盖了一切,可能是最简单/更正确(/可能更高效)的解决方案。

作为替代方案,下面的类提供了一个非常简单的界面(在可以忽略时捕获异常),同时使用户能够在需要时深入深入(如果适用效率,则获取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 ;
    }
}