Question

Dans un projet Android existant j'ai rencontré le morceau de code suivant (où j'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.toString()+" img: "+img.toString());
            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 pour le développement Android, et ayant googlé autour, je comprends que c'est la façon de faire des choses sans bloquer le thread principal (UI), tout en fixant toujours l'image sur le thread d'interface utilisateur après décodage. (au moins selon les développeurs androïde) (que j'ai vérifié par Thread.currentThread().getName() de l'exploitation forestière à divers endroits)

parfois l'image ne fonctionne tout simplement pas montrer, et stdout dit seulement

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

avec aucune trace des messages de la Runnable. Alors appearantly le Runnable ne run(), même si les rendements de img.post() true. Tirer le ImageView en onCreate() et déclarant final ne fait pas d'aide.

Je suis désemparés. Il suffit de mettre bitmap directement, tout en bloquant le thread d'interface utilisateur, fait des choses fixes, mais je veux bien faire les choses. Quelqu'un comprend ce qui se passe ici?

(ps. Ceci a été observé sur toutes les applications d'un téléphone 1.6 SDK androïde-3)

Était-ce utile?

La solution

Si vous regardez la documentation pour View.post il y a quelques informations pertinentes:

  

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

Depuis que vous faites cela en onCreate, il est probable que parfois votre View ne sera pas attaché à la fenêtre encore. Vous pouvez vérifier en surchargeant onAttachedToWindow et mettre quelque chose dans les journaux et vous connecter également lorsque vous postez. Vous verrez que lorsque le poste échoue, l'appel se produit avant après onAttachedToWindow.

Comme les autres l'ont mentionné, vous pouvez utiliser Activity.runOnUiThread ou fournir votre propre gestionnaire. Toutefois, si vous voulez le faire directement à partir du View lui-même, vous pouvez simplement obtenir le gestionnaire du View:

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

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

Autres conseils

J'étendu classe ImageView pour résoudre ce problème. Je collectionne les runnables passées à poster en vue non attaché à la fenêtre et post onAttachedToWindow recueillies runnable.

public class ImageView extends android.widget.ImageView
{
    List<Runnable> postQueue = new ArrayList<Runnable>();
    boolean attached;

    public ImageView(Context context)
    {
        super(context);
    }

    public ImageView(Context context, AttributeSet attrs)
    {
        super(context, attrs);
    }

    public ImageView(Context context, AttributeSet attrs, int defStyle)
    {
        super(context, attrs, defStyle);
    }

    @Override
    protected void onAttachedToWindow()
    {
        super.onAttachedToWindow();

        attached = true;

        for (Iterator<Runnable> 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é, ce qui est l'interface utilisateur de cette discussion. L'interface utilisateur ne peut être mis à jour par l'interface utilisateur de cette discussion.

Vous pouvez résoudre ce problème en utilisant Gestionnaire :

Handler uiHandler;

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

Ensuite, remplacer:

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

avec

uiHandler.post(new Runnable() {

pour vous assurer que le imageview est mis à jour dans l'interface utilisateur de cette discussion.

Handler est un concept tout à fait déroutant, j'ai aussi pris des heures de recherche pour comprendre vraiment à ce sujet;)

Je ne vois rien de mal à ce que de toute évidence, vous avez là-bas; appelant View.post () devrait l'amener à exécuter sur le thread d'interface utilisateur. Si votre activité est parti (peut-être par une rotation de l'écran), votre ImageView ne serait pas mis à jour, mais j'attendre encore une entrée de journal pour dire « la mise en bitmap ... », même si vous ne pouviez pas le voir.

Je vous conseille d'essayer ce qui suit et voir si cela fait une différence:

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

2) Faites glisser votre Runnable à Activity.runOnUiThread () plutôt que View.post ()

Utilisez le code suivant, peut poster votre code à MainThread à tout moment n'importe où, mais dépend pas de Context ou Activity. Cela peut prévenir l'échec view.getHandler() ou fastidieux onAttachedToWindow() étoffes etc.

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

J'ai eu le même problème, et en utilisant view.getHandler () a également échoué parce que le gestionnaire n'était pas présent. runOnUiThread () a résolu le problème. On peut supposer que cela ne fait en fait une file d'attente jusqu'à ce que l'interface utilisateur est prêt.

La cause me appelais la tâche de charge d'icône dans une classe de base et le résultat retourné si rapidement que la principale hadnt de classe estabished la vue (getView () dans le fragment).

Je suis un peu suspect qu'il pourrait échouer quelque temps spuriously. Mais je suis maintenant prêt pour cela! merci les gars.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top