Использование handler.postDelayed для

В принципе, я пытаюсь сделать что-то более сложное, но это моя проблема:

При использовании handler.postDelayed внутри цикла for задержка возникает только в первый раз, и я жду, когда задержка сработает каждый раз, когда цикл for повторяется:

Например, в этом случае:

    for(int z=0; z<4; z++) {
        final int finalZ = z;
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(play.this, "z:" + finalZ, Toast.LENGTH_SHORT).show();
            }
        }, 5000);
    }

Я получу:

  • Ожидание 5 секунд.
  • z:0
  • z:1
  • z:2
  • z:3

Что я хочу получить:

  • Ожидание 5 секунд.
  • z:0
  • Ожидание 5 секунд.
  • z:1
  • Ожидание 5 секунд.
  • z:2
  • Ожидание 5 секунд.
  • z:3

Мне сказали не использовать Thread.sleep(), потому что это может вызвать различные проблемы (я не совсем их понял, так как я новичок в студии Android). Я использую это в определенной деятельности (не в моей Main_Activity).

Я в основном пытаюсь изменить цвет изображения каждые 0,5 секунды или около того (в этом больше сложностей, но это основная идея). Будет ли Thread.sleep() лучше?

EDIT: Хорошо, благодаря @tynn и @pskink я заставил это работать (см. их ответы). Но теперь вслед за этим возникла другая проблема.

Если, например, после этого я буду запускать точно один и тот же код, они оба будут запускаться одновременно, а не друг за другом, как мне сделать так, чтобы второй "для" запускался только после первое "за" закончилось?


person Tom    schedule 25.03.2017    source источник
comment
замените 5000 на 5000 + z * 1000 например   -  person pskink    schedule 25.03.2017
comment
@pskink, можешь объяснить, почему?   -  person Tom    schedule 25.03.2017
comment
иметь значения параметра delayMillis 5000, 6000, 7000 и 8000 в четырех вызовах метода postDelayed - вот почему первый Runnable запускается через 5 секунд, второй через 6 секунд и так далее   -  person pskink    schedule 25.03.2017
comment
@pskink спасибо. Можете ли вы увидеть мой обновленный вопрос?   -  person Tom    schedule 25.03.2017


Ответы (3)


Здесь другой аспект

private int z=0;
Runnable r = new Runnable() {
            @Override
            public void run() {
                Toast.makeText(play.this, "z:" + z, Toast.LENGTH_SHORT).show();
                if(z<4){
                  z++;
                  handler.postDelayed(r, 5000);
                }
            }
        }
handler.post(r);
person misman    schedule 25.03.2017
comment
Спасибо, ловушка заключалась в том, чтобы просто поставить секунду с задержкой менее 5 секунд перед ее вводом. - person Tom; 26.03.2017
comment
Пожалуйста, я обновляю ответ. Я использую handler.post :) - person misman; 26.03.2017

Обработчик запускает ваш код в другом потоке, независимо от вашего цикла for. Задержка относится к моменту, когда вы разместили runnable. Не в сравнении с предыдущим постом. Также он вообще не блокирует цикл for. Это цель обработчиков.

Вы можете предположить, что любой пост в этом сценарии происходит одновременно, поэтому вы можете просто сделать handler.postDelayed(runnable, (z + 1) * 5000). Или, может быть, определить время выполнения как абсолютное с помощью handler.postAtTime(runnable, firstRun + z * 5000)

Но поскольку вы пытаетесь сделать что-то более сложное, вам лучше рассмотреть возможность использования Timer или аналогичный. Возможно, RxJava.

person tynn    schedule 25.03.2017
comment
@tynn handler.postDelayed(runnable, (z + 1) * 5000) действительно работает, но я не совсем понял, почему? Почему будут работать 2*5000, 3*5000 и т. д., а не 5000 все время? - person Tom; 25.03.2017
comment
@tynn спасибо, не могли бы вы увидеть мой обновленный вопрос? - person Tom; 25.03.2017
comment
Это та же причина, что и раньше. Задержка относится ко времени, когда вы звоните postDelayed(). Не относительно последнего опубликованного исполняемого файла. - person tynn; 26.03.2017

Ваш текущий код работает следующим образом:

  • Эй, обработчик, сделай это через 5 секунд
  • и сделайте это через 5 секунд,
  • и сделать это через 5 секунд,
  • и это, пожалуйста, сделайте через 5 секунд.

Затем мы ждем 5 секунд, и обработчик выполняет эти действия в один момент.

Что вы можете сделать, это изменить его на:

  • Эй, обработчик, сделай это через 5 секунд
  • ну и сделайте это через 10 секунд
  • и это через 15 секунд
  • ...

Или вы можете сделать это так:

  • Эй, обработчик, сделай это это через 5 секунд, где действие это – это "Эй, обработчик, сделай это через 5 секунд".

Затем обработчик будет ждать 5 секунд и вызовет действие «Эй, обработчик, сделай это через 5 секунд». Таким образом, обработчику придется снова подождать 5 секунд и выполнить какое-либо действие после этого времени.

Вот код для моего подхода:

final int finalZ = z;
delayedAction(handler, 0, z);
. . .
void delayedAction(final Handler handler, final int i, final int max) {
    if(i<max) {
        Toast.makeText(play.this, "z:" + finalZ, Toast.LENGTH_SHORT).show();
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                     delayedAction(handler, i+1, max);
            }
        });
    }
} 
person RadekJ    schedule 25.03.2017
comment
Спасибо! Я почти уверен, что буду использовать это в будущем, но на этот раз в своем коде я сделал что-то более простое :). Но помимо этого вы действительно помогли мне понять, как работает задержка, спасибо вам за это. - person Tom; 26.03.2017