Ответ 1
Это не вопрос, когда ArrayAdapter является потокобезопасным. ListView и другие подобные виджеты пользовательского интерфейса, которые работают с адаптером, не позволяют неожиданно изменять содержимое адаптера. И это больше, чем просто из-за других потоков - вам нужно сообщить 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, в своей функции выше вы можете сделать это, очистив текущие данные и добавив новые данные в теперь пустой адаптер. Это просто лишние накладные расходы, которые на самом деле ничего не приносят вам.