Runnable est posté avec succès mais pas exécuté

Dans un projet Android existant, j’ai rencontré le morceau de code suivant (où j’ai inséré la litière de débogage)

ImageView img = null; public void onCreate(...) { img = (ImageView)findViewById(R.id.image); new Thread() { public void run() { final Bitmap bmp = BitmapFactory.decodeFile("/sdcard/someImage.jpg"); System.out.println("bitmap: "+bmp.toSsortingng()+" img: "+img.toSsortingng()); if ( !img.post(new Runnable() { public void run() { System.out.println("setting bitmap..."); img.setImageBitmap(bmp); System.out.println("bitmap set."); } }) ) System.out.println("Runnable won't run!"); System.out.println("runnable posted"); } }.start(); 

Nouveau dans le développement Android, et après avoir parcouru les pages, je comprends que c’est la façon de faire les choses sans bloquer le thread principal (UI), tout en définissant l’image sur le thread d’interface utilisateur après le décodage. (au moins selon les développeurs Android) (que j’ai vérifié en enregistrant Thread.currentThread().getName() à différents endroits)

Maintenant, parfois, l’image ne s’affiche pas et stdout indique seulement

 I/System.out( 8066): bitmap: android.graphics.Bitmap@432f3ee8 img: android.widget.ImageView@4339d698 I/System.out( 8066): runnable posted 

avec pas une trace des messages du Runnable. Donc apparemment, le Runnable ne run() pas run() , bien que img.post() retourne true . onCreate() dans onCreate() et le déclarer final ne sert à rien.

Je suis désemparé. Le simple fait de définir le bitmap directement, tout en bloquant le thread de l’interface utilisateur, corrige les problèmes, mais je veux faire les choses correctement. Est-ce que quelqu’un comprend ce qui se passe ici?

(ps. tout cela a été observé sur un téléphone Android 1.6 et Android-3 sdk)

Si vous consultez la documentation de View.post vous View.post des informations pertinentes:

Cette méthode peut être invoquée de l’extérieur du thread d’interface utilisateur uniquement lorsque cette vue est attachée à une fenêtre.

Puisque vous faites cela dans onCreate , il est probable que parfois votre View ne soit pas encore attachée à la fenêtre. Vous pouvez le vérifier en onAttachedToWindow et en mettant quelque chose dans les journaux et en vous connectant également lorsque vous publiez. Vous verrez que lorsque la publication échoue, l’appel de publication a lieu avant onAttachedToWindow .

Comme les autres l’ont mentionné, vous pouvez utiliser Activity.runOnUiThread ou fournir votre propre gestionnaire. Cependant, si vous voulez le faire directement à partir de la View , vous pouvez simplement obtenir le gestionnaire de la vue:

 view.getHandler().post(...); 

Ceci est particulièrement utile si vous avez une vue personnalisée qui inclut une sorte de chargement en arrière-plan. Il y a aussi l’avantage supplémentaire de ne pas avoir à créer un nouveau gestionnaire distinct.

J’ai étendu la classe ImageView pour résoudre ce problème. Je collectionne les runnables passés à poster alors que la vue n’est pas attachée à la fenêtre et dans le message onAttachedToWindow collecté exécutable.

 public class ImageView extends android.widget.ImageView { List postQueue = new ArrayList(); boolean attached; public ImageView(Context context) { super(context); } public ImageView(Context context, AtsortingbuteSet attrs) { super(context, attrs); } public ImageView(Context context, AtsortingbuteSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); attached = true; for (Iterator posts = postQueue.iterator(); posts.hasNext();) { super.post(posts.next()); posts.remove(); } } @Override protected void onDetachedFromWindow() { attached = false; super.onDetachedFromWindow(); } @Override public boolean post(Runnable action) { if (attached) return super.post(action); else postQueue.add(action); return true; } } 

Je pense que le problème est que vous mettez à jour l’interface utilisateur (ImageView) avec un thread séparé, qui n’est pas le thread de l’interface utilisateur. L’interface utilisateur ne peut être mise à jour que par le thread d’interface utilisateur.

Vous pouvez résoudre ceci en utilisant Handler :

 Handler uiHandler; public void onCreate(){ ... uiHandler = new Handler(); // This makes the handler attached to UI Thread ... } 

Puis remplacez votre:

 if ( !img.post(new Runnable() { 

avec

 uiHandler.post(new Runnable() { 

pour vous assurer que l’imageview est mise à jour dans le thread d’interface utilisateur.

Handler est un concept assez déroutant, j’ai également pris des heures de recherche pour vraiment comprendre cela;)

Je ne vois rien de mal à ce que vous avez là-bas; appeler View.post () devrait provoquer son exécution sur le thread d’interface utilisateur. Si votre activité a disparu (peut-être par une rotation de l’écran), alors votre ImageView ne serait pas mis à jour, mais je m’attendrais toujours à ce qu’une entrée de journal dise “réglage bitmap …”, même si vous ne pouviez pas le voir.

Je suggère d’essayer ce qui suit et voir si cela fait une différence:

1) Utilisez Log.d (l’enregistreur Android standard) plutôt que System.out

2) Transmettez votre Runnable à Activity.runOnUiThread () plutôt qu’à View.post ()

Utilisez le code suivant, vous pouvez poster votre code sur MainThread à tout moment, mais ne dépend d’aucun Context ou Activity . Cela peut empêcher échec de view.getHandler() ou fastidieux sur les onAttachedToWindow() etc.

  new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() { //TODO } }); 

J’ai eu le même problème et l’utilisation de view.getHandler () a également échoué car le gestionnaire n’était pas présent. runOnUiThread () a résolu le problème. Vraisemblablement, cela fait effectivement quelques files d’attente jusqu’à ce que l’interface utilisateur soit prête.

La cause pour moi était d’appeler la tâche de chargement d’icône dans une classe de base et le résultat étant retourné si rapidement que la classe principale n’avait pas établi la vue (getView () dans fragment).

Je suis un peu sceptique quant à l’échec éventuel. Mais je suis maintenant prêt pour ça! Merci les gars.