Могу ли я выполнять сетевые операции (блокировка пользовательского интерфейса) внутри обработчиков/выполняемых модулей?

в моей mainActivity, которая является единственной активностью моего приложения, я создаю обработчик ниже и запускаю внутри него исполняемый файл.

У меня есть некоторые недоразумения относительно обработчиков и того, где они работают.

Посмотреть код

Handler handler;

    @Override
    protected void onCreate(Bundle bundle)
    {
      handler = new Handler();
      handler.postDelayed(r , 5000);
    }

    Runnable r = new Runnable()
    {
         @Override 
         public void run() {
              FetchServerAndUpdateStatus(); //network stuff in here
              handler.postDelayed(r , 5000);
         }
    }
  • Я предполагаю, что этот код все еще будет работать в потоке пользовательского интерфейса, и я не смогу совершать там какие-либо сетевые вызовы, нет?
  • Если да, то что мне делать? Создать и использовать отдельный поток?
  • Если я создал новый поток, как я могу запустить метод с отложенной записью? В теме нет отложенного поста?
  • Не лучше ли использовать обработчик/runnable и использовать TimerTask и Runnable? Или, как и вышеприведенный обработчик/исполняемый, он также будет работать в потоке пользовательского интерфейса, если только он не создан внутри отдельного потока.

person tony9099    schedule 25.09.2013    source источник
comment
Я думаю, что я что-то упускаю. У Handler действительно есть конструктор, который принимает runnable?   -  person joozek    schedule 25.09.2013
comment
@joozek нет, нет. postdelayed принимает хотя, мой плохой.   -  person tony9099    schedule 25.09.2013


Ответы (2)


Когда вы создаете обработчик, он привязывается к потоку, на котором он построен.

onCreate() выполняется в потоке пользовательского интерфейса, поэтому этот обработчик будет привязан к Looper в основном потоке и, следовательно, будет выполняться в этом потоке.

Если вам нужен обработчик, который вы можете использовать в другом потоке, вы можете создать его. См. документы Looper: https://developer.android.com/reference/android/os/Looper.html

Который имеет этот блок:

class LooperThread extends Thread {
  public Handler mHandler;

  public void run() {
      Looper.prepare();

      mHandler = new Handler() {
          public void handleMessage(Message msg) {
              // process incoming messages here
          }
      };

      synchronized (this) {
          this.notifyAll();
      }

      Looper.loop();
  }
}

Добавьте этот класс, а затем в onCreate сделайте следующее:

mLooperThread = new LooperThread();
mLooperThread.start();
synchronized (mLooperThread) {
  while (null == mLooperThread.mHandler) {
    mLooperThread.wait();
  }
}
mLooperThread.mHandler.postDelayed(r , 5000);

Это приведет к тому, что runnable будет запускаться НЕ в потоке пользовательского интерфейса, что, вероятно, вы и хотели.

Для задач, которые должны взаимодействовать с пользовательским интерфейсом, AsyncTask может быть лучше, поскольку он включает механизм запуска вещей, которые касаются представлений, когда задача выполняется в потоке пользовательского интерфейса, поскольку все, что касается представления, должно выполняться в потоке пользовательского интерфейса.

Другие механизмы для выполнения в потоке пользовательского интерфейса — публикация в самом представлении:

https://developer.android.com/reference/android/view/View.html#post(java.lang.Runnable) или [https://developer.android.com/reference/android/view/View.html#postDelayed(java.lang.Runnable, long)] (https://developer.android.com/reference/android/view/View.html#postDelayed(java.lang.Runnable, длинный))

Или попросить Activity запустить его в пользовательском интерфейсе для вас:

https://developer.android.com/reference/android/app/Activity.html#runOnUiThread(java.lang.Runnable)

person Nick Palmer    schedule 25.09.2013
comment
большое спасибо. Я думаю, что объяснение, которое вы дали, было самым точным и удобным для новичков. Я думаю, что многие люди думают, что при создании обработчика внутри onCreate() или onResume() они запускают его в отдельном потоке, что на самом деле не так. Существует большая путаница между обработчиками и потоками, что меня немного сбило с толку. Спасибо, что заполнили пробелы. - person tony9099; 26.09.2013
comment
Это вызовет: Попытка вызвать виртуальный метод 'boolean android.os.Handler.post(java.lang.Runnable)' для нулевой ссылки на объект. - person user2905416; 31.10.2019
comment
Да, при построении mHandler существует гонка, которой можно избежать, используя монитор на LooperThread. Я обновил код, чтобы решить эту проблему. - person Nick Palmer; 06.11.2019

  1. Это зависит от того, что вы делаете со своим обработчиком, вы не показали, как вы хотите получить m_handler. Если вы создадите его с помощью new Handler(Looper.getMainLooper()), он будет работать в потоке пользовательского интерфейса.
  2. Если вы хотите запускать код в фоновом режиме (сетевые операции), вам следует использовать AsyncTask
person joozek    schedule 25.09.2013
comment
Можете ли вы подробнее рассказать об Activty.getHandler(), где я могу получить его, кроме активности? Да, определенно я буду использовать сетевой материал, поэтому мне нужно, чтобы он работал в фоновом режиме. Приведенный выше код как есть, работает ли он в потоке пользовательского интерфейса или в другом потоке? - person tony9099; 25.09.2013
comment
Извините, я немного напутал. Вы можете получить основной looper, вызвав Looper.getMainLooper(). Looper — это то, что запускает код в циклах, вы можете прикрепить к нему обработчик с помощью new Handler(looper). Я все равно отредактирую свой ответ - person joozek; 25.09.2013
comment
Если я создам его без всего, как указано выше, где он будет работать? - person tony9099; 25.09.2013
comment
Документы говорят, что он будет запущен с привязкой к циклу к текущему потоку. Если вы (или кто-либо другой) не создавали цикл для текущего потока, то выдается исключение - person joozek; 25.09.2013