我遇到IllegalStateException将基础List更新为适配器(可能是ArrayAdapter或BaseAdapter的扩展,我不记得了)。我目前没有或记住了异常的文本,但它说明了List的内容发生变化,而没有得到适配器通知变更。
此列表/可以/从UI线程(主)以外的其他线程更新。更新此列表(添加项目)后,我调用notifyDataSetChanged。问题似乎是适配器或附加到适配器的ListView在调用此方法之前尝试更新自身。发生这种情况时,抛出IllegalStateException。
如果我在更新之前将ListView的可见性设置为GONE,那么再次可见,则不会发生错误。但这并不总是实用的。
我在某处读到你无法从另一个线程修改底层 - 这似乎限制了一个MVC模式,就像这个特定的List一样,我想从不同的线程中添加项目。我假设只要我调用notifyDataSetChanged()我就是安全的 - 在调用此方法之前,适配器没有重新访问底层列表,但似乎并非如此。
我想我要问的是,从UI以外的线程更新基础List是否安全?此外,如果我想修改适配器中的数据,我是否修改基础列表或适配器本身(通过其add()等方法)。通过适配器修改数据似乎是错误的。
我在另一个网站上遇到了一个似乎与我有类似问题的人:http://osdir.com/ml/Android-Developers/2010-04/msg01199.html(这是我抓住了Visibility.GONE和.VISIBLE的想法)。
为了让您更好地了解我的特定问题,我将介绍一下我的列表,适配器等的设置方法。
我有一个名为Queue的对象,它包含一个LinkedList。队列扩展了Observable,当通过其方法将事物添加到其内部列表时,我调用setChanged()和notifyListeners()。此Queue对象可以在任意数量的线程中添加或删除项目。
我有一个包含适配器的“队列视图”活动。此活动在其onCreate()方法中将Observer侦听器注册到我的Queue对象。在Observer的update()方法中,我在Adapter上调用notifyDataSetChanged()。
我添加了大量的日志输出并确定当发生IllegalStateExcption时,我的Observer回调从未被调用过。因此,在观察者有机会通知其观察者之前,适配器注意到List的更改,并调用我的方法通知适配器内容已更改。
所以我想我要问的是,这是一个装配适配器的好方法吗?这是一个问题,因为我正在从UI线程以外的线程更新适配器的内容吗?如果是这种情况,我可能会想到一个解决方案(在创建时将Queue对象赋予UI线程,并使用该Handler进行所有List修改,但这似乎不合适。)
我意识到这是一个非常开放的帖子,但我对此感到有些失落,并会对我写的内容有任何评论。
答案 0 :(得分:8)
此列表/可能/可从中更新 除UI之外的另一个线程 线程(主要)
那不行。
我读到某个你不能的地方 修改底层的 另一个线程 - 这似乎是 限制MVC模式,就像这样 特别列表,我想添加项目 来自不同的线程
MVC与线程无关。
可以安全地更新 来自其他线程的底层列表 比UI?
没有。其他线程可以触发对适配器的更新(例如,通过post()
),但是必须在主应用程序线程上处理更新本身,对于当前附加到ListView
的适配器。
另外,如果我想修改 适配器中的数据,我可以修改吗? 基础列表或适配器 本身(通过其add()等方法)。 通过适配器修改数据 似乎错了。
您可以Adapter
通过Adapter
修改ArrayAdapter
。您可以通过Adapter
的基础数据库/内容提供商修改CursorAdapter
。其他适配器可能会有所不同。
我有一个名为Queue的对象 包含LinkedList。队列扩展 可观察的,以及添加的东西 通过它的内部列表 方法,我调用setChanged()和 notifyListeners()。
您是否考虑过使用LinkedBlockingQueue
,而不是实施自己的线程安全Queue
?
此活动,在其onCreate() 方法,注册一个Observer监听器 到我的队列对象。在观察者的 我打电话给update()方法 适配器上的notifyDataSetChanged()。
Adapters
应该自己调用notifyDataSetChanged()
(如果他们做出更改)或者让正在更改数据的实体调用它们(例如,Cursor
为CursorAdapter
一个Activity
)。 那是MVC。 ArrayAdapter
在您的数据模型发生变化时既不知道也不关心。
所以就好像适配器注意到了 列表在观察者之前的变化 有机会通知其观察员,并且 调用我的方法通知适配器 内容已经改变。
可能你正在使用ArrayAdapter
,在这种情况下,所有这些额外的观察者/通知内容都会妨碍你,因为这是为你处理的。您只需要安排更新主应用程序线程上的Handler
。
所以我想我要问的是,是 这是装配适配器的好方法吗?
不是特别,恕我直言。
这是一个问题,因为我正在更新 适配器的内容来自一个线程 除了UI线程?
如果您没有强制更新回主应用程序线程,一旦您解决了其他问题,这最终会崩溃。
给Queue对象一个Handler UI线程创建时,make 所有List修改使用它 处理程序,但这似乎不合适
您可以使用post()
,也可以在附加的ListView
上致电ArrayAdapter
。
关闭袖口,我将创建一个名为ThreadSafeArrayAdapter
的{{1}}子类,并使用它代替Queue
。 ThreadSafeArrayAdapter
将add()
,insert()
和remove()
替换为具有超类在主应用程序线程上执行其操作的Handler
或{{1} }}
答案 1 :(得分:0)
总的好建议是http://developer.android.com/resources/articles/painless-threading.html
我个人使用自定义线程(扩展线程的类),但通过Message发送响应给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更容易。