2010-05-27 5 views
12

J'ai un thread distinct en cours d'exécution pour obtenir des données à partir d'Internet. Après cela, je voudrais mettre à jour le ListView dans le thread principal en appelant adapt.notifyDataSetChanged(). Mais ça ne marche pas. Une solution de contournement pour cela? Merci.Mettre à jour ListView dans le fil principal à partir d'un autre fil

+0

http://stackoverflow.com/a/33584826/1318946 –

Répondre

12

bons conseils Dans l'ensemble est http://android-developers.blogspot.com/2009/05/painless-threading.html

Personnellement, j'utiliser mon fil personnalisé (un fil d'extension de classe), mais envoyer une réponse au fil de l'interface utilisateur par un message. Ainsi, dans la fonction run() du thread, il y a:

Message msg; 
msg = Message.obtain(); 
msg.what = MSG_IMG_SET;      
mExtHandler.sendMessage(msg); 

Le thread UI définit un gestionnaire de messages.

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); 
     } 
    };     

Ceci est réellement plus facile que AsyncTask.

+0

Je préfère aussi cette méthode – Mark

+0

J'ai oublié de mentionner: vous pouvez également ajouter un objet au message comme: msg.obj = someBitmap; et le recevoir dans le gestionnaire (qui peut par exemple mettre à jour un tableau ou une liste avec le bitmap reçu et notifier à l'adaptateur que les données sous-jacentes de la liste ont changé: mArrayAdapter.notifyDataSetChanged();). Dans ce cas, aucune condition de course ne peut se produire. – Yar

+2

Attention - Voici des dragons! Cette classe Handler interne a une référence implicite à la classe externe 'this. Mieux vaut rendre ce Handler statique et prendre une Faible Référence à la classe extérieure. –

9

Ou, envoyer un message à la file d'attente de messages du listview (qui exécuter sur le thread d'interface utilisateur):

list.post(new Runnable() {     
    @Override 
    public void run() { 
     adapter.notifyDataSetChanged(); 

    } 
}); 
2

On suppose yourActivity est l'activité que votre widget a été placé en elle, yourView est la widget et adapter est l'adaptateur du widget:

yourActivity.runOnUiThread(new Runnable() { 
public void run() {  
     try{ 
       adapter.notifyDataSetChanged(); 
     } 
     catch{} 
} 
} 
3

Vous pouvez utiliser une combinaison de RxJava et Retrofit à cet effet.

Voici comment vous pouvez le faire. Je vais utiliser l'API GitHub pour l'exemple.

Créez une interface Java pour votre API HTTP. L'utilisation de Observable permet de convertir la réponse en un flux.

public interface GitHubService { 
    @GET("users/{user}/repos") 
    Observable<List<Repo>> listRepos(@Path("user") String user); 
} 

Chaque événement étant une liste de repos.

Utilisez la classe Retrofit pour générer une implémentation de l'interface GitHubService. Vous pouvez ou ne pouvez pas fournir un client HTTP personnalisé.

Retrofit retrofit = new Retrofit.Builder() 
      .baseUrl("https://api.github.com/") 
      .client(okHttpClient) // OkHttp Client 
      .addConverterFactory(GsonConverterFactory.create()) 
      .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) 
      .build(); 

GitHubService service = retrofit.create(GitHubService.class); 

Maintenant vient la partie Rx. Vous devez ajouter un abonné pour écouter les réponses renvoyées par le serveur. En d'autres termes, réagissent à lui.

service.listRepos("octocat") 
.subscribeOn(Schedulers.io()) // 1 
.observeOn(AndroidSchedulers.mainThread()) // 2 
.flatMap(Observable::from) // 3 
.subscribe(repoList -> { // 4 
    // Update the list view 
    // notifyDataSetChanged 
}); 

Voici ce que les lignes commentées avec des chiffres font -

1 - Il raconte l'OS qui se faufilent à utiliser pour effectuer l'appel. Nous choisissons le thread IO pour cela.

2 - Indique au système d'exploitation le thread à utiliser pour écouter la réponse. Nous faisons cela sur le thread principal, et mettre à jour l'interface utilisateur à la réception d'une réponse.

3 - Cette ligne démontre la vraie magie de Rx. Cette petite ligne simple convertit une liste de réponses en objets uniques. Par conséquent, nous réagirons à chaque objet au lieu de toute la liste. Vous pouvez en lire plus à ce sujet here.

4 - Cette ligne définit réellement le type de 'réaction' qui sera montré à l'événement. Vous pouvez mettre à jour l'adaptateur ici.

Un abonné plus complexe ressemble à ceci -

new Subscriber<Repo>() { 
    @Override 
    public void onCompleted() { 

    } 

    @Override 
    public void onError(Throwable e) { 

    } 

    @Override 
    public void onNext(Repo repo) { 

    } 
} 

post-scriptum - J'ai utilisé les lambdas dans les exemples ci-dessus. Vous pouvez ajouter cela par here.

-1

L'astuce ici est la position où vous mettez les

mAdapter.notifyDataSetChanged();

Voici un exemple simple:

mRecyclerView = (RecyclerView) rootView.findViewById(R.id.recycler_view); 
mAdapter = new myAdapter(......); 
mAdapter.notifyDataSetChanged(); 
mRecyclerView.setAdapter(mAdapter); 

ce travail pour moi parfaitement.

Questions connexes