Android - как передать данные в Runnable в runOnUiThread? - PullRequest
23 голосов
/ 14 октября 2011

Мне нужно обновить некоторый пользовательский интерфейс и сделать это внутри потока пользовательского интерфейса, используя runOnUiThread
. Теперь данные для пользовательского интерфейса поступают из другого потока, представленного здесь data.

Как я могу передать данные в Runnable, чтобы их можно было использовать для обновления пользовательского интерфейса?Android, кажется, не позволяет использовать данные напрямую.Есть ли элегантный способ сделать это?

public void OnNewSensorData(Data data) {

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

Моим решением было создание fioeld private Data sensordata внутри исполняемого файла и присвоение ему данных.Это работает, только если оригинал Data data является окончательным.

public void OnNewSensorData(final Data data) {

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

Ответы [ 6 ]

36 голосов
/ 14 октября 2011

Проблема, которую вы обнаружили, заключается в том, что

Внутренние классы в Java захватывают ("закрывают") лексическую область, в которой они определены.Но они только фиксируют переменные, которые объявлены как «окончательные».

Если это ясно как грязь, здесь есть хорошее обсуждение деталей: Невозможно обратиться к неконечной переменной внутри внутреннего класса, определенного в другом методе

Но ваше решение выглядит хорошо.Кроме того, при условии, что data является окончательным, вы можете упростить код до следующего:

public void OnNewSensorData(final Data data) {
    runOnUiThread(new Runnable() {
        public void run() {
            // use data here
            data.doSomething();
        }
    });
}
21 голосов
/ 14 октября 2011

Если вы хотите избежать использования промежуточной конечной переменной (как описано Dan S), вы можете реализовать Runnable с дополнительным методом для установки данных:

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

Затем вы можете вызвать метод следующим образом:

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

вы также можете заставить конструктор MyRunnable принимать экземпляр Data в качестве аргумента:

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

  public void run() {
    ...
  }
}

, а затем просто скажите runOnUiThread (new MyRunnable (data));

5 голосов
/ 25 сентября 2013

У меня была похожая проблема, когда я хотел передать информацию в поток.Чтобы решить эту проблему с помощью системы Android, я изменил ответ corsiKa следующим образом: Runnable с параметром?

Вы можете объявить класс прямо в методе и передать параметр, как показано ниже:

void Foo(String str) {
    class OneShotTask implements Runnable {
        String str;
        OneShotTask(String s) { str = s; }
        public void run() {
            someFunc(str);
        }
    }
    runOnUiThread(new OneShotTask(str));
}
1 голос
/ 02 ноября 2015

Вот типичный случай, когда сервисный обратный вызов вызывается для обновления строки состояния пользовательского интерфейса (TextView textStatus). Сервис может быть многопоточным.

Пример объединяет проверку необходимости перенаправления потока и фактического перенаправления:

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

См. Также Как проверить, работает ли в потоке пользовательского интерфейса в Android?

1 голос
/ 14 октября 2011

Вам нужно будет обновлять каждый раз, когда ваша программа имеет новую Data, которую она хочет показать. Ваш второй список кодов здесь является стандартным способом сделать это. Могут быть некоторые ловушки, если вы продолжаете обновлять Data в теме. Если это так, рассмотрите возможность блокировки потока до тех пор, пока пользовательский интерфейс не завершит обновление или копирование данных в другой объект Data.

Внутри происходит то, что JVM копирует ссылку на объект Data для запуска анонимного класса. Data хранится внутри, все еще может быть изменено. Если ваш метод требует дополнительных изменений в Data, просто используйте другую переменную (ссылку на объект), например: final Data finalData = data;. Вы также можете удалить строку private Data sensordata = data; и использовать данные непосредственно в методе run.

Это может выглядеть не элегантно, но именно так Java передает объектные переменные анонимным классам. Существует более новый синтаксис в Java Language версии 7, но Android совместим с Java Language version 5 и 6.

0 голосов
/ 06 апреля 2013
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();
        }
    });
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...