Il ne s'agit pas d'ArrayAdapter étant thread-safe. ListView et d'autres widgets d'interface utilisateur fonctionnant avec un adaptateur n'autorisent pas le changement inattendu du contenu de l'adaptateur. Et ce n'est pas seulement dû à d'autres threads: vous devez indiquer à ListView la modification que vous effectuez avant d'essayer d'interagir avec votre adaptateur. Si vous autorisez un autre thread à modifier l'adaptateur, ou si vous le modifiez sur le thread principal sans en avertir ListView avant d'autoriser quoi que ce soit d'autre, vous obtiendrez au hasard (en raison des courses) des exceptions lancées par ListView à propos de l'adaptateur change de façon inattendue.
Dans le cas spécifique de ArrayAdapter si vous utilisez l'API pour modifier son contenu, il se chargera de dire à la vue de liste sur la modification. Cependant vous devez faire de tels changements sur le thread principal, pour vous assurer que la vue de liste n'essaie pas d'accéder à l'adaptateur entre le point où votre modification est faite et la vue de liste est informée de ce changement.
Si vous effectuez des modifications simples à ArrayAdapter (en ajoutant et supprimant quelques éléments), tout ira bien, mais vous devez le faire sur le thread principal. Pour des changements plus importants (comme dire que l'adaptateur obtient un nouvel ensemble de données en raison d'une extraction de nouvelles données à partir d'un serveur), ne pas utiliser ArrayAdapter et implémenter votre propre sous-classe de BaseAdapter. ArrayAdapter est conçu pour les situations où vous avez un petit ensemble de données assez statique pour montrer des situations simples. Pour les choses plus compliquées, vous serez probablement plus content d'implémenter BaseAdapter et de faire vous-même la gestion des données. La manière typique dont un adaptateur est mis à jour dans ces situations compliquées est qu'un thread d'arrière-plan génère le nouvel ensemble de données, et une fois qu'il est disponible, il est alors atomisé sur l'adaptateur avec un appel à notifyDataSetChanged() pour laisser le ListView savoir que les données ont changé. Donc, disons que vous montrez des données qui sont un tableau d'objets MyItem.Gardez vos données dans un tableau de jeu:
ArrayList<MyItem>
Mettre en oeuvre une sous-classe de BaseAdapter qui montre cette liste:
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);
}
...
}
Et maintenant, voici une fonction que vous pourriez mettre en œuvre sur l'adaptateur qui peut être appelé à partir d'une autre fil pour fournir une nouvelle série de données à afficher:
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();
}
});
}
Bien sûr, si vous utilisez AsyncTask pour faire votre génération de données ce qui a déjà une installation pratique pour effectuer cette travailler sur le fil principal. De même, vous pouvez utiliser la nouvelle fonctionnalité Loader pour prendre en charge la génération en arrière-plan. Et si vous voulez toujours utiliser ArrayAdapter, dans votre fonction ci-dessus, vous pouvez le faire en effaçant les données actuelles et en ajoutant les nouvelles données à l'adaptateur maintenant vide. C'est juste plus de frais généraux qui ne vous apporte vraiment rien.
bien, résolu mon problème. THX – 2red13