我整天都在研究这个问题,我已经准备好把头发拉出来。我在这里和网上找到了一些答案,说这是因为尝试在线程中使用View(而不是在UI线程中)做某事。但是我已经尝试了所有的想法(处理程序/新线程),我已经看到它仍然无法让它工作。我作为业余爱好在C编程多年,现在我是Java / Android的新手。我正在使用Eclipse和Android 2.1平台进行编程。我希望我的应用程序能够与尽可能多的Android手机一起工作,我认为我使用的所有功能都与API 1兼容。我还看到有一些叫做AsyncTask的东西,但会导致问题有旧电话的人?
所以这就是我的应用程序所做的。当我点击一个按钮时,该应用程序上线到一个网站并下载一个xml / rss源。然后它解析它并使用我创建的自定义适配器将数据放入listview。下载和解析可能需要1秒到15秒,所以我想添加一个进度对话框。添加之后,这就是我开始在这篇文章的标题中收到错误消息的地方。该应用程序成功下载(我的示例网络上的xml文件中有8条记录,因此它非常小)但是我在列表视图显示之前看到了错误。所以我想我需要确切地知道视图的哪个部分导致错误,然后如何解决它。
这是代码(我已经删除了过去几个小时内的所有测试代码,所以它很干净,而且对你们所有人来说都不那么混乱......和我一起):
@SuppressWarnings("serial")
public class ClubMessageList extends ListActivity implements Serializable
{
private static final String TAG = "DGMS News";
private ArrayList<CMessage> m_messages = null;
private MessageAdapter m_adapter;
private ProgressDialog m_ProgressDialog = null;
private Runnable downloadMessages;
// Need handler for callbacks to the UI thread
final Handler mHandler = new Handler();
@SuppressWarnings("unchecked")
@Override
public void onCreate(Bundle icicle)
{
Log.i(TAG, "Starting the ClubMessageList activity");
super.onCreate(icicle);
setContentView(R.layout.list);
setTitle("DGMS News - Clubs");
try
{
// check to see if we already have downloaded messages available in the bundle
m_messages = (ArrayList<CMessage>) ((icicle == null) ? null : icicle.getSerializable("savedMessages"));
// if there are no messages in the bundle, download them from the web and then display them
if (m_messages == null)
{
m_messages = new ArrayList<CMessage>();
this.m_adapter = new MessageAdapter(this, R.layout.row_club, (ArrayList<CMessage>) m_messages);
setListAdapter(this.m_adapter);
downloadMessages = new Runnable(){
public void run() {
getMessages();
}
};
Thread thread = new Thread(null, downloadMessages, "DownloadMessages");
thread.start();
m_ProgressDialog = ProgressDialog.show(ClubMessageList.this,
"Please wait...", "Retrieving 2010 Show data ...", true);
}
else // messages were already downloaded, so display them in the listview (don't download them again)
{
Log.i("DGMS News", "Starting activity again. Data exists so don't retrieve it again.");
m_adapter = new MessageAdapter(this, R.layout.row_club, (ArrayList<CMessage>) m_messages);
this.setListAdapter(m_adapter);
}
}
catch (Throwable t)
{
Log.e("DGMS News",t.getMessage(),t);
}
}
private Runnable returnRes = new Runnable()
{
public void run()
{
if(m_messages != null && m_messages.size() > 0)
{
m_adapter.notifyDataSetChanged();
for(int i=0;i<m_messages.size();i++)
m_adapter.add(m_messages.get(i));
}
m_ProgressDialog.dismiss();
m_adapter.notifyDataSetChanged();
}
};
private void getMessages()
{
try
{
m_messages = new ArrayList<CMessage>();
ClubFeedParser parser = ClubFeedParserFactory.getParser();
m_messages = parser.parse();
for(int i = 0; i < m_messages.size(); i++)
m_adapter.add(m_messages.get(i));
}
catch (Exception e)
{
Log.e("DGMS News", e.getMessage());
}
runOnUiThread(returnRes);
}
protected void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
outState.putSerializable("savedMessages", (Serializable) m_messages);
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id)
{
super.onListItemClick(l, v, position, id);
Intent intent = new Intent(ClubMessageList.this, ClubDetails.class);
// Add all info about the selected club to the intent
intent.putExtra("title", m_messages.get(position).getTitle());
intent.putExtra("location", m_messages.get(position).getLocation());
intent.putExtra("website", m_messages.get(position).getLink());
intent.putExtra("email", m_messages.get(position).getEmail());
intent.putExtra("city", m_messages.get(position).getCity());
intent.putExtra("contact", m_messages.get(position).getContact());
intent.putExtra("phone", m_messages.get(position).getPhone());
intent.putExtra("description", m_messages.get(position).getDescription());
startActivity(intent);
}
private class MessageAdapter extends ArrayAdapter<CMessage> implements Serializable
{
private ArrayList<CMessage> items;
public MessageAdapter(Context context, int textViewResourceId, ArrayList<CMessage> items)
{
super(context, textViewResourceId, items);
this.items = items;
}
public View getView(int position, View convertView, ViewGroup parent)
{
View v = convertView;
if (v == null)
{
LayoutInflater vi = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.row_club, null);
CMessage m = items.get(position);
if (m != null)
{
TextView ltt = (TextView) v.findViewById(R.id.ltoptext);
TextView rtt = (TextView) v.findViewById(R.id.rtoptext);
TextView lbt = (TextView) v.findViewById(R.id.lbottext);
if (ltt != null)
ltt.setText(m.getTitle());
if (rtt != null)
rtt.setText(m.getLocation());
if (lbt != null)
lbt.setText(m.getCity() + ", CO");
//if (rbt != null)
; // not used in this list row
}
}
return v;
}
}
}
正如我所说,所有这些代码都运行良好,直到我添加了几天前在其他网站上找到的进度对话框。
我感谢任何和所有的帮助,虽然我已经去了Android开发者网站查看线程,处理程序等,但它让我越来越困惑。实际的代码更改会很棒。 :-)今天看了这么多网站后,我的头脑受伤了。
谢谢!
鲍勃
答案 0 :(得分:2)
runOnUiThread(new Runnable() {
public void run() {
m_adapter.notifyDataSetChanged();
m_adapter.add(m_messages.get(i));
m_ProgressDialog.dismiss();
m_adapter.notifyDataSetChanged();
}
});
您的所有Ui代码都必须在runOnUiThread中。你得到的错误是你试图从活动UI线程以外的其他线程更新UI。
代码中的这个线程导致了问题。
private Runnable returnRes = new Runnable()
{
public void run()
{
if(m_messages != null && m_messages.size() > 0)
{
m_adapter.notifyDataSetChanged();
for(int i=0;i<m_messages.size();i++)
m_adapter.add(m_messages.get(i));
}
m_ProgressDialog.dismiss();
m_adapter.notifyDataSetChanged();
}
};
答案 1 :(得分:0)
使用AsyncTask。目前几乎没有手机正在使用Android 1.0 / 1.1(唯一一款搭载Android&lt; 1.5的手机是HTC G1,而且大部分手机都是在很久以前升级的OTA)。如果您确实需要支持这些设备,则可以使用相同的UserTask
。有关详细信息,请参阅this article。并查看有关确保从主线程更新UI的十几个SO问题。