什么是更新适配器底层数据的最佳实践方式?

我遇到一个IllegalStateException更新一个底层的List到适配器(可能是一个ArrayAdapter或BaseAdapter的扩展,我不记得)。 我目前还没有或记得例外的文本,但是它说明了在没有通知适配器变化的情况下List的内容改变的效果。

这个List / may /可以从UI线程(main)以外的另一个线程更新。 我更新这个列表(添加一个项目)后,我打电话给notifyDataSetChanged。 问题似乎是适配器或连接到适配器的ListView尝试在调用此方法之前自行更新。 发生这种情况时,抛出IllegalStateException。

如果在更新之前将ListView的可见性设置为GONE,那么再次可见,不会发生错误。 但是这并不总是实际的。

我读了一个地方,你不能修改从另一个线程的底层 – 这似乎限制了一个MVC模式,就像这个特定的列表,我想添加来自不同线程的项目。 我认为,只要我调用notifyDataSetChanged()我是安全的 – 该适配器没有重新访问基础列表,直到这个方法被调用,但似乎并非如此。

我想我问的是,从UI以外的线程更新底层列表是否可以安全? 此外,如果我想修改适配器中的数据,是否修改底层List或适配器本身(通过add()等方法)。 通过适配器修改数据似乎是错误的。

我遇到了另一个网站上的一个线程似乎有一个类似的问题,我的: http : //osdir.com/ml/Android-Developers/2010-04/msg01199.html (这是从我抓住Visibility.GONE和.VISIBLE的想法)。

为了让您更好地了解我的特定问题,我将介绍一下如何设置List,Adapter等。

我有一个名为Queue的对象,它包含一个LinkedList。 队列扩展了Observable,当通过它的方法把事物添加到内部列表中时,我调用setChanged()和notifyListeners()。 此队列对象可以添加或从任意数量的线程中删除项目。

我有一个包含适配器的“队列视图”活动。 这个活动,在其onCreate()方法,注册一个观察者监听器到我的队列对象。 在Observer的update()方法中,我调用适配器上的notifyDataSetChanged()。

我添加了很多日志输出,并确定当这个IllegalStateExcption发生我的观察者callback从未被调用。 所以就好像Adapter在Observer有机会通知Observers之前注意到了List的变化,并且调用我的方法来通知Adapter告知内容已经改变了。

所以我想我问的是,这是一个适配器的好方法吗? 这是一个问题,因为我正在更新从UI线程以外的线程适配器的内容? 如果是这种情况,我可能有一个解决scheme(创build时,给队列对象一个处理程序的UI线程,并使用该处理程序的所有列表修改,但这似乎不正确)。

我意识到这是一个非常开放的post,但是我对此有点失落,并希望对我写的东西有任何意见。

Solutions Collecting From Web of "什么是更新适配器底层数据的最佳实践方式?"

这个List / may /可以从UI线程(主)以外的另一个线程更新

这是行不通的。

我读了一个地方,你不能从另一个线程修改底层这个 – 这似乎限制了一个MVC模式,就像这个特定的列表,我想添加来自不同线程的项目

MVC与线程无关。

从UI以外的线程更新底层列表可以安全吗?

不可以。其他线程可以触发适配器的更新(例如,通过post() ),但更新本身必须在主应用程序线程上处理,以适应当前连接到ListView的适配器。

此外,如果我想修改适配器中的数据,是否修改底层List或适配器本身(通过add()等方法)。 通过适配器修改数据似乎是错误的。

您可以通过Adapter本身为ArrayAdapter修改Adapter 。 您可以通过CursorAdapter的基础数据库/内容提供程序修改Adapter 。 其他适配器可能有所不同。

我有一个名为Queue的对象,它包含一个LinkedList。 队列扩展了Observable,当通过它的方法把东西添加到内部列表中时,我调用setChanged()和notifyListeners()。

你有没有考虑使用LinkedBlockingQueue ,而不是实现自己的线程安全的Queue

这个活动,在其onCreate()方法,注册一个观察者监听器到我的队列对象。 在Observer的update()方法中,我调用适配器上的notifyDataSetChanged()。

Adapters应该自己调用notifyDataSetChanged() (如果更改是由它们完成的话),或者由更改数据的实体(例如CursorAdapterCursor )调用它们。 是MVC。 数据模型更改时, Activity不应该知道也不在乎。

所以就好像Adapter在Observer有机会通知Observers之前注意到了List的变化,并且调用我的方法来通知Adapter告知内容已经改变了。

可能你正在使用一个ArrayAdapter ,在这种情况下,所有这些额外的观察者/通知的东西正在你的方式,因为这是为你处理。 您只需要安排更新主应用程序线程上的ArrayAdapter

所以我想我问的是,这是一个适配器的好方法吗?

不是特别的,恕我直言。

这是一个问题,因为我正在更新从UI线程以外的线程适配器的内容?

如果你不强迫更新回到主应用程序线程,那么一旦清除了其他问题,这最终会崩溃。

在创build时将Queue对象的Handler赋给UI线程,并使用Handler对所有的List进行修改,但这看起来不合适

你可以使用一个Handler ,或者你可以在你的ListView上调用post()

closures袖口,我会创build一个名为ThreadSafeArrayAdapterArrayAdapter的子类,并用它来代替QueueThreadSafeArrayAdapter通过Handlerpost() ThreadSafeArrayAdapteradd()insert()remove()replace为具有超类在主应用程序线程上执行的操作。

总的来说,好的build议是http://developer.android.com/resources/articles/painless-threading.html

我个人使用我的自定义线程(扩展线程类),但通过消息发送响应到UI线程。 所以在线程的run()函数中有:

 Message msg; msg = Message.obtain(); msg.what = MSG_IMG_SET; mExtHandler.sendMessage(msg); 

mExtHandler被分配给线程构造函数中的外部处理程序实例。 UI线程定义消息处理程序:

 private Handler mImagesProgressHandler; public void onCreate(Bundle bundle) { mImagesProgressHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case LoadImagesThread.MSG_IMG_SET: mArrayAdapter.setBitmapList(mImagesList); mArrayAdapter.notifyDataSetChanged(); break; case LoadImagesThread.MSG_ERROR: break; } super.handleMessage(msg); } }; 

这实际上比AsyncTask更容易。