ArrayAdapter线程在android中安全吗?如果没有,我该怎么做才能确保线程安全



假设我扩展了ArrayAdapter,在覆盖getView(int i, View v, ViewGroup g)的代码中,我使用getItem(i)检索当前项。我能确定即使其他线程操作相同的ArrayAdaptergetItem(i)也会返回一个项目吗?

我不确定,但我认为答案是否定的。如果是,你建议我怎么做才能确保它的线程安全?

这不是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,在上面的函数中,您可以通过清除当前数据并将新数据添加到现在为空的适配器中来实现这一点。这只是更多的开销,并没有真正给你带来任何好处。

阵列适配器不是线程安全的。我看到它由于并发问题而崩溃。阵列适配器只会将您的阵列转换为主(GUI)线程上的视图。因此,如果您小心地只在主线程上更改数组(添加或删除),那么您可以确保它只在1个线程上运行一次。

最新更新