让我说我扩展ArrayAdapter
并在我覆盖getView(int i, View v, ViewGroup g)
的代码中,我使用getItem(i)
检索当前项目。即使其他线程操纵相同的getItem(i)
,我能确定ArrayAdapter
会返回一个项吗?
我不确定,但我认为答案是肯定的。如果是的话,你建议我做些什么来使它成为线程安全的?
答案 0 :(得分:29)
ArrayAdapter不是线程安全的问题。与适配器一起使用的ListView和其他此类UI小部件不允许适配器的内容在其上意外更改。这不仅仅是由于其他线程 - 您需要告诉ListView您在下次尝试与适配器交互之前所做的更改。
如果你允许另一个线程修改适配器,或者在主线程上修改它,但在允许其他任何事情发生之前不告诉ListView有关更改,你将随机(由于比赛)得到ListView抛出的异常适配器意外更改。
在ArrayAdapter的特定情况下,如果您使用API修改其内容,它将负责告诉列表视图有关更改。但是,您必须在主线程上进行此类更改,以确保列表视图不会尝试在进行更改的位置之间访问适配器,并且列表视图被告知该更改
如果你只是对ArrayAdapter进行简单的更改(添加和删除一些项目),那么你会没事的,但你必须在主线程上做这些。
对于更重要的更改(例如,由于从服务器获取新数据,适配器获取新数据集),请考虑不使用ArrayAdapter,而是实现自己的BaseAdapter子类。 ArrayAdapter适用于需要显示一小组简单的静态数据集的情况 - 简单情况。对于更复杂的事情,您可能更乐意实现BaseAdapter并自己进行数据管理。
适配器在这些复杂情况下更新的典型方式是后台线程生成新数据集,一旦可用,然后在主线程上通过调用notifyDataSetChanged()以原子方式交换到适配器让ListView知道数据已经改变。
因此,假设您正在显示一些MyItem对象数组的数据。将数据保存在播放阵列中:
ArrayList<MyItem>
实现显示此列表的BaseAdapter的子类:
class MyAdapter extends BaseAdapter<MyItem> {
ArrayList<MyItem> mItems;
public int getCount() {
return mItems != null ? mItems.size() : 0;
}
public MyItem getItem(int position) {
return mItems.get(i);
}
...
}
现在,这是一个可以在适配器上实现的函数,可以从另一个线程调用以提供要显示的新数据集:
final Handler mHandler = new Handler();
void setDataFromAnyThread(final ArrayList<MyItem> newData) {
// Enqueue work on mHandler to change the data on
// the main thread.
mHandler.post(new Runnable() {
@Override
public void run() {
mItems = newData;
notifyDataSetChanged();
}
});
}
当然,如果您使用AsyncTask进行数据生成,这已经具有在主线程上执行该工作的便利工具。同样,您可以使用新的Loader工具来处理后台生成。
如果您仍想使用ArrayAdapter,在上面的函数中,您可以通过清除当前数据并将新数据添加到现在空的适配器来完成此操作。这只是更多的开销,并没有真正获得任何东西。
答案 1 :(得分:5)
Array Adapter不是线程安全的。我看到它因并发问题而崩溃。数组适配器只会将您的数组转换为主(GUI)线程上的视图。因此,如果您只小心在主线程上更改数组(添加或删除),那么您可以确保它只会在1个线程上运行。