我是开发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("\\<.*?>", "");
}
}
答案 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
内,分别从其onPreExecute
和onPostExecute
方法显示/隐藏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 ;
}
}