Как запустить поток Runnable в Android через определенные промежутки времени?

Я разработал приложение для отображения некоторого текста через определенные промежутки времени на экране эмулятора Android. Я использую класс Handler. Вот фрагмент моего кода:

handler = new Handler();
Runnable r = new Runnable() {
    public void run() {
        tv.append("Hello World");               
    }
};
handler.postDelayed(r, 1000);

Когда я запускаю это приложение, текст отображается только один раз. Почему?

Ответ 1

Простое решение для вашего примера:

handler = new Handler();

final Runnable r = new Runnable() {
    public void run() {
        tv.append("Hello World");
        handler.postDelayed(this, 1000);
    }
};

handler.postDelayed(r, 1000);

Или мы можем использовать обычный поток, например (с оригинальным Runner):

Thread thread = new Thread() {
    @Override
    public void run() {
        try {
            while(true) {
                sleep(1000);
                handler.post(this);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
};

thread.start();

Вы можете рассматривать свой работоспособный объект так же, как команду, которая может быть отправлена ​​в очередь сообщений для выполнения, а обработчик - только вспомогательный объект, используемый для отправки этой команды.

Подробнее здесь http://developer.android.com/reference/android/os/Handler.html

Ответ 2

new Handler().postDelayed(new Runnable() {
    public void run() {
        // do something...              
    }
}, 100);

Ответ 3

Я думаю, что может улучшить первое решение Alex2k8 для обновления, исправляющего каждую секунду

1. Исходный код:

public void run() {
    tv.append("Hello World");
    handler.postDelayed(this, 1000);
}

2. Анализ

  • В приведенной выше стоимости предположим tv.append("Hello Word") стоимость T миллисекунды, после отображения 500 время с задержкой 500 * T миллисекунды
  • Он будет увеличиваться с задержкой при длительном прогоне

3. Решение

Чтобы избежать этого Просто измените порядок postDelayed(), чтобы избежать задержки:

public void run() {
    handler.postDelayed(this, 1000);
    tv.append("Hello World");
}

Ответ 4

Для повторной задачи вы можете использовать

new Timer().scheduleAtFixedRate(task, runAfterADelayForFirstTime, repeaingTimeInterval);

назовите его как

new Timer().scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {

            }
        },500,1000);

Вышеприведенный код запускается впервые после половины секунды (500) и повторяется после каждого второго (1000)

Где

задача - выполняемый метод

после время до первоначального выполнения

( интервал время повторения выполнения)

Во-вторых

И вы также можете использовать CountDownTimer, если вы хотите выполнить количество задач раз.

    new CountDownTimer(40000, 1000) { //40000 milli seconds is total time, 1000 milli seconds is time interval

     public void onTick(long millisUntilFinished) {
      }
      public void onFinish() {
     }
    }.start();

//Above codes run 40 times after each second

И вы также можете сделать это с помощью runnable. создать метод runnable, например

Runnable runnable = new Runnable()
    {
        @Override
        public void run()
        {

        }
    };

И назовите это двумя способами

new Handler().postDelayed(runnable, 500 );//where 500 is delayMillis  // to work on mainThread

ИЛИ

new Thread(runnable).start();//to work in Background 

Ответ 5

Я считаю, что для этого типичного случая, то есть для запуска чего-то с фиксированным интервалом, Timer более уместен. Вот простой пример:

myTimer = new Timer();
myTimer.schedule(new TimerTask() {          
@Override
public void run() {
    // If you want to modify a view in your Activity
    MyActivity.this.runOnUiThread(new Runnable()
        public void run(){
            tv.append("Hello World");
        });
    }
}, 1000, 1000); // initial delay 1 second, interval 1 second

Использование Timer имеет несколько преимуществ:

  • Начальная задержка и интервал могут быть легко указаны в аргументах schedule
  • Таймер можно остановить, просто позвонив myTimer.cancel()
  • Если вы хотите запустить только один поток, не забудьте вызвать myTimer.cancel() перед планированием нового (если myTimer не равен null)

Ответ 6

Handler handler=new Handler();
Runnable r = new Runnable(){
    public void run() {
        tv.append("Hello World");                       
        handler.postDelayed(r, 1000);
    }
}; 
handler.post(r);

Ответ 7

Если я правильно понимаю документацию метода Handler.post():

Заставляет Runnable r добавляться в очередь сообщений. Runnable будет запущен в потоке, к которому прикреплен этот обработчик.

Таким образом, примеры, предоставленные @alex2k8, хотя и работают правильно, не совпадают. В случае, если используется Handler.post(), не создаются новые потоки. Вы просто отправляете Runnable в поток с Handler, который будет выполняться EDT. После этого EDT выполняет только Runnable.run(), ничего больше.

Помните: Runnable != Thread.

Ответ 8

Интересным примером является то, что вы можете постоянно видеть, как счетчик/секундомер работает в отдельном потоке. Также показано GPS-местоположение. Хотя основной поток пользовательского интерфейса уже существует.

Выдержки:

try {    
    cnt++; scnt++;
    now=System.currentTimeMillis();
    r=rand.nextInt(6); r++;    
    loc=lm.getLastKnownLocation(best);    

    if(loc!=null) { 
        lat=loc.getLatitude();
        lng=loc.getLongitude(); 
    }    

    Thread.sleep(100); 
    handler.sendMessage(handler.obtainMessage());
} catch (InterruptedException e) {   
    Toast.makeText(this, "Error="+e.toString(), Toast.LENGTH_LONG).show();
}

Чтобы посмотреть код, см. здесь:

Пример потока, показывающий местоположение GPS и текущее время, выполняемое вместе с потоком пользовательского интерфейса основной активности

Ответ 9

Если вы хотите быть уверенным, что Handler будет прикреплен к основному потоку, вы должны инициализировать его следующим образом:

new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
    public void run() {
        // do something...              
    }
}, 100);

Ответ 10

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

class SimpleRunnable: Runnable {
    public override fun run() {
        println("${Thread.currentThread()} has run.")
    }
}
fun main(args: Array<String>) {
    val thread = SimpleThread()
    thread.start() // Will output: Thread[Thread-0,5,main] has run.
    val runnable = SimpleRunnable()
    val thread1 = Thread(runnable)
    thread1.start() // Will output: Thread[Thread-1,5,main] has run
}

Ответ 11

Котлин

private lateinit var runnable: Runnable
override fun onCreate(savedInstanceState: Bundle?) {
    val handler = Handler()
    runnable = Runnable {
        // do your work
        handler.postDelayed(runnable, 2000)
    }
    handler.postDelayed(runnable, 2000)
}

Джава

Runnable runnable;
Handler handler;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    handler = new Handler();
    runnable = new Runnable() {
        @Override
        public void run() {
            // do your work
            handler.postDelayed(this, 1000);
        }
    };
    handler.postDelayed(runnable, 1000);
}