我使用AsyncTask打开URL,访问服务器,获取内容并在主活动的列表视图中显示它们。提取的内容包括报纸的标题和网站的URL,如果"读取"将在第二个活动中显示在WebView上。单击按钮。我直接编写了程序并且它可以工作,但是当我回头看它时,我发现了一些似乎不合理的东西,所以主要是我想弄清楚代码是如何工作的。以下是主要活动的代码:
package com.example.newsapp;
public class MainActivity extends Activity {
static final private String LOG_TAG = "main";
private ArrayList<Content> aList;
private class Content{
Content() {};
public String title;
public String url;
}
private class MyAdapter extends ArrayAdapter<Content>{
int resource;
public MyAdapter(Context _context, int _resource, List<Content> titles) {
super(_context, _resource, titles);
resource = _resource;
// this.context = _context;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
LinearLayout newView;
final Content content = getItem(position);
// Inflate a new view if necessary.
if (convertView == null) {
newView = new LinearLayout(getContext());
String inflater = Context.LAYOUT_INFLATER_SERVICE;
LayoutInflater vi = (LayoutInflater) getContext().getSystemService(inflater);
vi.inflate(resource, newView, true);
} else {
newView = (LinearLayout) convertView;
}
// Fills in the view.
TextView tv = (TextView) newView.findViewById(R.id.listText);
ImageButton b = (ImageButton) newView.findViewById(R.id.listButton);
b.setBackgroundResource(0);
tv.setText(content.title);
Typeface type = Typeface.createFromAsset(getAssets(),"LiberationSerif-BoldItalic.ttf");
tv.setTypeface(type);
// Sets a listener for the button, and a tag for the button as well.
b.setTag(Integer.toString(position));
b.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Reacts to a button press.
Intent intent = new Intent(MainActivity.this, WebPage.class);
Bundle bundle = new Bundle();
bundle.putString("URL", content.url);
intent.putExtras(bundle);
startActivity(intent);
}
});
return newView;
}
}
class MyAsyncTask extends AsyncTask<String, String, String> {
private ProgressDialog progressDialog = new ProgressDialog(MainActivity.this);
InputStream inputStream = null;
String result = "";
Content content;
protected void onPreExecute() {
super.onPreExecute();
progressDialog.setMessage("Downloading the news...");
progressDialog.show();
progressDialog.setOnCancelListener(new OnCancelListener() {
public void onCancel(DialogInterface arg0) {
MyAsyncTask.this.cancel(true);
}
});
}
@Override
protected String doInBackground(String... params) {
String url_select = params[0];
ArrayList<NameValuePair> param = new ArrayList<NameValuePair>();
try {
// Set up HTTP post
// HttpClient is more then less deprecated. Need to change to URLConnection
HttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(url_select);
httpPost.setEntity(new UrlEncodedFormEntity(param));
HttpResponse httpResponse = httpClient.execute(httpPost);
HttpEntity httpEntity = httpResponse.getEntity();
// Read content & Log
inputStream = httpEntity.getContent();
} catch (UnsupportedEncodingException e1) {
Log.e("UnsupportedEncodingException", e1.toString());
e1.printStackTrace();
} catch (ClientProtocolException e2) {
Log.e("ClientProtocolException", e2.toString());
e2.printStackTrace();
} catch (IllegalStateException e3) {
Log.e("IllegalStateException", e3.toString());
e3.printStackTrace();
} catch (IOException e4) {
Log.e("IOException", e4.toString());
e4.printStackTrace();
}
// Convert response to string using String Builder
try {
BufferedReader bReader = new BufferedReader(new InputStreamReader(inputStream, "iso-8859-1"), 8);
StringBuilder sBuilder = new StringBuilder();
String line = null;
while ((line = bReader.readLine()) != null) {
sBuilder.append(line + "\n");
}
inputStream.close();
result = sBuilder.toString();
} catch (Exception e) {
Log.e("StringBuilding & BufferedReader", "Error converting result " + e.toString());
}
return result;
} // protected Void doInBackground(String... params)
protected void onPostExecute(String result) {
//parse JSON data
try {
super.onPostExecute(result);
Log.i(LOG_TAG, result);
JSONObject object = new JSONObject(result);
JSONArray jArray = object.getJSONArray("sites");
for(int i=0; i < jArray.length(); i++) {
JSONObject jObject = jArray.getJSONObject(i);
content = new Content();
if (jObject.has("title") && jObject.has("url")){
content.title = jObject.getString("title");
content.url = jObject.getString("url");
aList.add(content);
aa.notifyDataSetChanged();
}
} // End Loop
progressDialog.dismiss();
} catch (JSONException e) {
// progressDialog.dismiss();
Log.e("JSONException", "Error: " + e.toString());
}
} // protected void onPostExecute(String result)
}
private MyAdapter aa;
private MyAsyncTask loadTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
loadTask = new MyAsyncTask();
loadTask.execute("http://luca-ucsc.appspot.com/jsonnews/default/news_sources.json");
aList = new ArrayList<Content>();
aa = new MyAdapter(this, R.layout.list_element, aList);
ListView myListView = (ListView) findViewById(R.id.listView1);
myListView.setAdapter(aa);
aa.notifyDataSetChanged();
}
public void refresh(View v){
if (loadTask.getStatus() == AsyncTask.Status.FINISHED){
aList.clear();
aa.notifyDataSetChanged();
new MyAsyncTask().execute("http://luca-ucsc.appspot.com/jsonnews/default/news_sources.json");
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
}
因此,您只能在loadTask.execute()
onCreate()
之后看到,我是否为alist和aa创建了对象,但我已经在AsyncTaks的onPostExecute()
中使用了它们class,所以我不太清楚这里发生了什么,因为onPostExecute()
和UI在同一个线程上,所以onPostExecute()
中的代码应该先执行。
我以为我应该把
aList = new ArrayList<Content>();
aa = new MyAdapter(this, R.layout.list_element, aList);
进入onPostExecute()
,这对我来说更符合逻辑,但应用程序会以这种方式崩溃。另外我认为删除aa.notifyDataSetChanged();
中的onPostExecute()
不应该是一个问题,因为它也在onCreate()
方法中,但这实际上会导致列表视图为空白,没有任何内容。实际上,将loadTask.execute()
之后的任何代码放入onPostExecute()
方法的if块会导致某些问题,或者导致应用崩溃。如果有人可以提供一些见解或提示,那将是很好的。谢谢你的阅读。
答案 0 :(得分:4)
onPostExecute
。您无法保证与UI线程上的其他调用相关的此调用的时间。
由于您已经自己实施getView
,因此我建议您扩展BaseAdapter
而不是ArrayAdapter
,并实施其他一些必需的方法。它并不难,您可以使用任何数据结构来支持适配器。假设您使用List<Content>
来支持适配器,您可以编写一个方法来交换列表,如下所示:
public void swapList(List<Content> newList) {
this.list = newList;
notifyDataSetChanged();
}
在AsyncTask
中,您可以完全控制Params,Progress和Result参数化类型。他们不必都是String
。你可以这样做:
private class myAsyncTask extends AsyncTask<String, Void, List<Content>> {
/* ... */
}
Params的String
是URL(与您现在相同)。 Void
for Progress,因为您无论如何都不会发布进度。 List<Content>
表示结果,因为这是您在完成任务后最终想要的结果。
您应该在doInBackground
中完成所有工作。没有理由将String反序列化为JSONArray并在onPostExecute
中混淆,特别是因为这发生在主线程上。重写doInBackground
以返回List<Content>
,onPostExecute中您需要的只是:
public void onPostExecute(List<Content> result) {
adapter.swapList(result);
}
现在您可以创建一次适配器(在onCreate()
中),只需在适当的时候交换列表。