web-dev-qa-db-fra.com

Android - comment passer des données au Runnable dans runOnUiThread?

J'ai besoin de mettre à jour une interface utilisateur et de le faire à l'intérieur du fil de l'interface utilisateur en utilisant runOnUiThread 
Maintenant, les données de l'interface utilisateur proviennent de l'autre thread, représenté par data ici.

Comment puis-je transmettre les données au Runnable, afin qu’elles puissent être utilisées pour mettre à jour l’UI? Y a-t-il une manière élégante de faire ceci?

public void OnNewSensorData(Data data) {

    runOnUiThread(new Runnable() {
        public void run() {
            //use data
        }
    });
}

Ma solution consistait à créer un __filaire private Data sensordata à l'intérieur du runnable et à lui affecter des données. Cela ne fonctionne que si le Data data original est final.

public void OnNewSensorData(final Data data) {

    runOnUiThread(new Runnable() {
        private Data sensordata = data;
        public void run() {
            //use sensordata which is equal to data
        }
    });
}
22
Skip

Le problème que vous avez trouvé est que

Les classes internes en Java capturent ("le proche") la portée lexicale dans laquelle ils sont définis. Mais ils ne capturent que les variables déclarées "finales". 

Si cela est clair en tant que boue, il y a une bonne discussion des détails ici: Impossible de faire référence à une variable non finale à l'intérieur d'une classe interne définie dans une méthode différente

Mais votre solution a l'air bien. De plus, à condition que data soit final, vous pouvez simplifier le code comme suit:

public void OnNewSensorData(final Data data) {
    runOnUiThread(new Runnable() {
        public void run() {
            // use data here
            data.doSomething();
        }
    });
}
35
spatulamania

Si vous souhaitez éviter d'utiliser une variable finale intermédiaire (comme décrit par Dan S), vous pouvez implémenter Runnable avec une méthode supplémentaire pour définir les données:

public class MyRunnable implements Runnable {
  private Data data;
  public void setData(Data _data) {
    this.data = _data;
  }

  public void run() {
    // do whatever you want with data
  }
}

Vous pouvez alors appeler la méthode comme ceci:

public void OnNewSensorData(Data data) {
  MyRunnable runnable = new MyRunnable();
  runnable.setData(data);
  runOnUiThread(runnable);
}

vous pouvez également faire en sorte que le constructeur de MyRunnable prenne l'instance de données comme argument:

public class MyRunnable implements Runnable {
  private Data data;
  public MyRunnable(Data _data) {
    this.data = _data;
  }

  public void run() {
    ...
  }
}

puis dites simplement runOnUiThread (new MyRunnable (data));

21
aleph_null

J'ai eu un problème similaire où je voulais transmettre des informations dans le fil. Pour le résoudre avec le système Android, je modifie la réponse de corsiKa dans: Runnable avec un paramètre?

Vous pouvez déclarer une classe directement dans la méthode et transmettre le paramètre comme indiqué ci-dessous:

void Foo(String str) {
    class OneShotTask implements Runnable {
        String str;
        OneShotTask(String s) { str = s; }
        public void run() {
            someFunc(str);
        }
    }
    runOnUiThread(new OneShotTask(str));
}
5
James Oravec

Voici un cas typique où le rappel de service est appelé pour mettre à jour une chaîne d'état de l'interface utilisateur (TextView textStatus). Le service peut être fileté.

L'exemple combine vérifie si la redirection de thread est nécessaire et la redirection réelle:

   // service callbacks
    public void service_StatusTextChanged(final String s) {
        if( isOnUiThread() ) {
            textStatus.setText(s);
        } else {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    textStatus.setText(s);
                }
            });
        }
    }

    static public boolean isOnUiThread() {
       return Thread.currentThread() == Looper.getMainLooper().getThread();
    }

Voir aussi Comment vérifier si vous utilisez un thread d'interface utilisateur sous Android?

1
user1656671

Vous devrez mettre à jour chaque fois que votre programme a la nouvelle Data qu'il veut afficher. Votre deuxième liste de code ici est le moyen standard pour accomplir cela. Il peut y avoir certaines captures si vous continuez à mettre à jour Data dans le fil de discussion. Si tel est le cas, envisagez de bloquer le thread jusqu'à ce que l'interface utilisateur ait fini de mettre à jour ou de copier les données dans un autre objet Data.

En interne, la machine virtuelle Java copie la référence à l'objet Data pour le moment où la classe anonyme sera exécutée. Data stocké à l'intérieur peut toujours être changé. Si votre méthode nécessite des modifications supplémentaires à Data, utilisez simplement une autre variable (référence d'objet) telle que: final Data finalData = data;. Vous pouvez également supprimer la ligne private Data sensordata = data; et utiliser les données directement dans votre méthode d'exécution.

Cela peut ne pas sembler élégant, mais c’est ainsi que Java transmet les variables d’objet aux classes anonymes. La version 7 du langage Java est plus récente, mais Android est compatible avec les versions 5 et 6 du langage Java.

1
Dan S
public static Activity globalContext = null;    
CommonSetting.globalContext = this;// put this in MainACtivity.onCreate()   
public void createToastShort(final String message) {
    runOnUiThread(new Runnable() {
        public void run() {
         Toast.makeText(CommonSetting.globalContext, message, Toast.LENGTH_SHORT).show();
        }
    });
}
0
hamish