bro*_*oli 6 android adapter notify android-listview
我写了一个ListActivity,它有一个自定义列表适配器.运行onCreate时,将从ContentProvider更新列表.我还有一个服务,在我运行应用程序时启动,它首先更新ContentProvider,然后发送内容已更新的广播.
我的ListActivity接收广播并尝试更新我的ListView.我的问题是,我收到有关ListView适配器数据更改的间歇性错误,而不通知ListView.我notifyDataSetChanged()更新后立即调用我的列表适配器上的方法.看起来似乎正在发生的事情是,当它接收来自服务的广播以进行更新时,列表仍然在第一次调用onCreate之后进行更新,因此它尝试更新我的ListView,直到它从第一次运行完成更新.这有意义吗?这是我的一些代码.
注意:该服务正常运行,它获取新数据并更新我的ContentProvider,并且在更新时我确实在我的活动中获得了广播.
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ctx = this;
getPrefs();
setContentView(R.layout.main);
// Setup preference listener
preferences = PreferenceManager.getDefaultSharedPreferences(this);
preferences.registerOnSharedPreferenceChangeListener(listener);
// Setup report list adapter
ListView nzbLv = (ListView) findViewById(R.id.report_list);
nzbla = new NZBReportListAdaptor(ctx);
getReports();
nzbla.setListItems(report_list);
nzbLv.setAdapter(nzbla);
// Broadcast receiver to get notification from NZBService to update ReportList
registerReceiver(receiver,
new IntentFilter(NZBService.BROADCAST_ACTION));
startService(new Intent(ctx, NZBService.class));
}
@Override
public void onResume() {
super.onResume();
timerHandler.resume();
new updateSabQueue().execute();
//updateList();
}
@Override
public void onPause() {
super.onPause();
timerHandler.pause();
unregisterReceiver(receiver);
}
private BroadcastReceiver receiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
Toast.makeText(ctx, "NZBService broadcast recieved", Toast.LENGTH_SHORT).show();
updateReportList();
}
};
private void updateReportList() {
new updateReportList().execute();
}
private class updateReportList extends AsyncTask<Void, Void, Boolean> {
/* (non-Javadoc)
* @see android.os.AsyncTask#onPreExecute()
* Show progress dialog
*/
protected void onPreExecute() {
}
/* (non-Javadoc)
* @see android.os.AsyncTask#doInBackground(Params[])
* Get new articles from the internet
*/
protected Boolean doInBackground(Void...unused) {
getReports();
return true;
}
/**
* On post execute.
* Close the progress dialog
*/
@Override
protected void onPostExecute(Boolean updated) {
if (updated) {
Log.d(TAG, "NZB report list adapter updated");
synchronized(this) {
nzbla.setListItems(report_list);
}
Log.d(TAG, "NZB report list notified of change");
nzbla.notifyDataSetChanged();
}
}
}
Run Code Online (Sandbox Code Playgroud)
既然已经回答了这个问题,我将发布我的更新代码,以帮助可能遇到它的其他人.
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ctx = this;
getPrefs();
setContentView(R.layout.main);
// Setup preference listener
preferences = PreferenceManager.getDefaultSharedPreferences(this);
preferences.registerOnSharedPreferenceChangeListener(listener);
// Setup report list adapter
ListView nzbLv = (ListView) findViewById(R.id.report_list);
nzbla = new NZBReportListAdaptor(ctx);
report_list.addAll(getReports());
nzbla.setListItems(report_list);
nzbLv.setAdapter(nzbla);
// Broadcast receiver to get notification from NZBService to update ReportList
registerReceiver(receiver,
new IntentFilter(NZBService.BROADCAST_ACTION));
startService(new Intent(ctx, NZBService.class));
}
private class updateReportList extends AsyncTask<Void, Void, ArrayList<Report>> {
/* (non-Javadoc)
* @see android.os.AsyncTask#onPreExecute()
* Show progress dialog
*/
protected void onPreExecute() {
}
/* (non-Javadoc)
* @see android.os.AsyncTask#doInBackground(Params[])
* Get new articles from the internet
*/
protected ArrayList<Report> doInBackground(Void...unused) {
return getReports();
}
/**
* On post execute.
* Close the progress dialog
*/
@Override
protected void onPostExecute(ArrayList<Report> updated) {
nzbla.setListItems(updated);
nzbla.notifyDataSetChanged();
}
}
private ArrayList<Report> getReports() {
ArrayList<Report> reports = new ArrayList<Report>();
ContentResolver r = getContentResolver();
Cursor c = r.query(NZBReportProvider.CONTENT_URI, null, null, null, NZBReportProvider.ARTICLE_KEY_ROWID + " DESC");
startManagingCursor(c);
Log.d(TAG, "NZBReport cursor.getCount=" + c.getCount());
int title = c.getColumnIndex(NZBReportProvider.ARTICLE_KEY_TITLE);
int desc = c.getColumnIndex(NZBReportProvider.ARTICLE_KEY_DESCRIPTION);
int cat = c.getColumnIndex(NZBReportProvider.ARTICLE_KEY_CAT);
int size = c.getColumnIndex(NZBReportProvider.ARTICLE_KEY_SIZE);
int link = c.getColumnIndex(NZBReportProvider.ARTICLE_KEY_LINK);
int catid = c.getColumnIndex(NZBReportProvider.ARTICLE_KEY_CATID);
int date = c.getColumnIndex(NZBReportProvider.ARTICLE_KEY_DATE_ADDED);
int group = c.getColumnIndex(NZBReportProvider.ARTICLE_KEY_GROUP);
if (c.getCount() > 0) {
c.moveToFirst();
do {
URL url = null;
try {
url = new URL(c.getString(link));
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
reports.add(new Report(c.getString(title), url, c.getString(desc), c.getString(cat), c.getString(date), c.getString(size), c.getInt(catid), c.getString(group)));
} while (c.moveToNext());
}
return reports;
}
Run Code Online (Sandbox Code Playgroud)
Ric*_*ler 11
您必须在UI线程上更新Adapter数据,因此不需要同步块.它也没用,因为你的AsyncTask每次执行时都会创建新的同步.
另一个问题是你在notifyDataSetChanged外面打电话给Adapter.您应该在setListItems方法结束时调用它.这不应该导致错误,因为它是在UI线程上执行但不应该以这种方式调用.
您应该确保您的getReports方法不Adapter以任何方式修改后备存储.由于它在单独的线程上运行,因此无法修改任何Adapter有权访问的内容.即使它受到锁的保护.您需要做的是在您的doInBackground方法中生成更新列表或新列表等,onPostExecute然后将其传递到其中,然后将新数据提交到AdapterUI线程上.所以,如果你的getReports功能正在发生变化report_list而你Adapter的参考就是report_list你做错了.getReports必须创建一个新的report_list,然后Adapter在UI线程上完成创建后将其传递给您.
重申一下,您只能在UI线程上修改数据Adapter,然后才能ListView访问.使用同步/锁定不会更改此要求.
| 归档时间: |
|
| 查看次数: |
39513 次 |
| 最近记录: |