android异步任务和UI线程交互
我使用AsyncTask打开URL、访问服务器、获取内容并在主活动的列表视图中显示它们。提取的内容包括报纸标题和网站的URL,如果单击“阅读”按钮,这些内容将在第二个活动的WebView上显示。我直接编写了这个程序,它可以工作,但是当我回顾它时,我发现一些似乎不合理的地方,所以我主要想弄清楚代码是如何工作的。以下是主要活动的代码:android异步任务和UI线程交互,android,android-listview,android-asynctask,Android,Android Listview,Android Asynctask,我使用AsyncTask打开URL、访问服务器、获取内容并在主活动的列表视图中显示它们。提取的内容包括报纸标题和网站的URL,如果单击“阅读”按钮,这些内容将在第二个活动的WebView上显示。我直接编写了这个程序,它可以工作,但是当我回顾它时,我发现一些似乎不合理的地方,所以我主要想弄清楚代码是如何工作的。以下是主要活动的代码: package com.example.newsapp; public class MainActivity extends Activity { sta
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;
}
}
进入
onPostExecute()
,这对我来说更符合逻辑,但应用程序会以这种方式崩溃。我还认为删除aa.notifyDataSetChanged()onPostExecute()
中的code>应该没有问题,因为它也在onCreate()
方法中,但这实际上会导致列表视图为空,没有任何内容。实际上,将loadTask.execute()
之后的任何代码放入onPostExecute()
方法的if块会导致一些问题,或者导致应用程序崩溃。如果有人能给出一些见解或提示,那就太好了。感谢阅读。后台任务完成工作后,在UI线程上调用onPostExecute
。您无法保证此调用相对于UI线程上的其他调用的计时
由于您已经自己实现了getView
,因此我建议您扩展BaseAdapter
,而不是ArrayAdapter
,并实现其他几个必需的方法。这并不难,您可以使用任何数据结构来支持适配器。假设您使用一个列表
来支持适配器,您可以编写一个方法来交换列表,如下所示:
public void swapList(List<Content> newList) {
this.list = newList;
notifyDataSetChanged();
}
Params的字符串
是URL(与您现在所做的相同)<代码>作废以获取进度,因为您无论如何都不会发布进度<代码>列表以获得结果,因为这是您在完成任务后真正想要得到的结果
您应该在doInBackground
中完成所有工作。没有理由将字符串反序列化为JSONArray并与onPostExecute
中的字符串混淆,特别是因为这是在主线程上发生的。重写doInBackground
以返回列表
,onPostExecute中所需的全部内容如下:
public void onPostExecute(List<Content> result) {
adapter.swapList(result);
}
public void onPostExecute(列表结果){
适配器。swapList(结果);
}
现在,您可以创建一次适配器(在
onCreate()
)并在适当的时候交换列表。onPostExecute
在后台任务完成后在UI线程上调用。您无法保证此调用相对于UI线程上的其他调用的计时
由于您已经自己实现了getView
,因此我建议您扩展BaseAdapter
,而不是ArrayAdapter
,并实现其他几个必需的方法。这并不难,您可以使用任何数据结构来支持适配器。假设您使用一个列表
来支持适配器,您可以编写一个方法来交换列表,如下所示:
public void swapList(List<Content> newList) {
this.list = newList;
notifyDataSetChanged();
}
Params的字符串
是URL(与您现在所做的相同)<代码>作废以获取进度,因为您无论如何都不会发布进度<代码>列表以获得结果,因为这是您在完成任务后真正想要得到的结果
您应该在doInBackground
中完成所有工作。没有理由将字符串反序列化为JSONArray并与onPostExecute
中的字符串混淆,特别是因为这是在主线程上发生的。重写doInBackground
以返回列表
,onPostExecute中所需的全部内容如下:
public void onPostExecute(List<Content> result) {
adapter.swapList(result);
}
public void onPostExecute(列表结果){
适配器。swapList(结果);
}
现在,您可以创建一次适配器(在
onCreate()
)并在适当的时候交换列表。但是您必须在onPostExecute()
方法中更新UI
。@PiYusHGuPtA我认为aa.notifyDataSetChanged()会更新UI,但您必须在onPostExecute()中更新UI
)
method.@PiYusHGuPtA我认为aa.notifyDataSetChanged()更新UIonPostExecute,向主线程的运行循环发送消息。这将由主线程在准备就绪时执行。很可能在onCreate完成后。非常感谢您的解释。是的,我认为将背景工作和出版物分开绝对是一种更好、更干净的方法。但我仍然不明白为什么不能用旧方法在onPostExecute方法中创建arraylist和adapter对象。您能解释一下为什么在这里扩展BaseAdapter更好吗?我在基类中找不到list字段,所以我还需要重写notifyDataSetChanged()方法?谢谢。您不需要重写notifyDataSetChanged()
,只要在更改支持适配器的数据集时调用它即可。我只是建议扩展BaseAdapter
,以便更好地控制其行为,因为您已经实现了getView()
,这实际上是适配器的核心。如果愿意,您可以在每次onPostExecute
中创建一个新适配器。onPostExecute会向主线程的运行循环发送一条消息。这将由主线程在准备就绪时执行。很可能在onCreate完成后。非常感谢您的解释。是的,我认为将背景工作和出版物分开绝对是一种更好、更干净的方法。但我仍然不明白为什么不能用旧方法在onPostExecute方法中创建arraylist和adapter对象。您能解释一下为什么在这里扩展BaseAdapter更好吗?我在基类中找不到list字段,所以我还需要重写notifyDataSetChanged()方法?谢谢。你不必担心