Повторить задачу с задержкой? - PullRequest
195 голосов
/ 05 июня 2011

В моем коде есть переменная, которая говорит, что это «статус».

Я хочу отобразить некоторый текст в приложении в зависимости от значения этой переменной.Это должно быть сделано с определенной временной задержкой.

Это как,

  • Проверить значение переменной состояния

  • Показать некоторыетекст

  • Подождите 10 секунд

  • Проверьте значение переменной состояния

  • Отобразите некоторый текст

  • Подождите 15 секунд

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

Я попытался Thread.sleep(time delay), и это не удалось.Есть ли лучший способ сделать это?

Ответы [ 11 ]

425 голосов
/ 05 июня 2011

Для этой цели вы должны использовать функцию Handler postDelayed.Он будет запускать ваш код с указанной задержкой в ​​основном потоке пользовательского интерфейса, поэтому вы сможете обновить элементы управления пользовательского интерфейса.

private int mInterval = 5000; // 5 seconds by default, can be changed later
private Handler mHandler;

@Override
protected void onCreate(Bundle bundle) {

    // your code here

    mHandler = new Handler();
    startRepeatingTask();
}

@Override
public void onDestroy() {
    super.onDestroy();
    stopRepeatingTask();
}

Runnable mStatusChecker = new Runnable() {
    @Override 
    public void run() {
          try {
               updateStatus(); //this function can change value of mInterval.
          } finally {
               // 100% guarantee that this always happens, even if
               // your update method throws an exception
               mHandler.postDelayed(mStatusChecker, mInterval);
          }
    }
};

void startRepeatingTask() {
    mStatusChecker.run(); 
}

void stopRepeatingTask() {
    mHandler.removeCallbacks(mStatusChecker);
}
32 голосов
/ 09 января 2013

Для всех, кто заинтересован, вот класс, который я создал с использованием кода inazaruk, который создает все необходимое (я назвал его UIUpdater, потому что я использую его для периодического обновления пользовательского интерфейса, но вы можете называть его как угодно):

import android.os.Handler;
/**
 * A class used to perform periodical updates,
 * specified inside a runnable object. An update interval
 * may be specified (otherwise, the class will perform the 
 * update every 2 seconds).
 * 
 * @author Carlos Simões
 */
public class UIUpdater {
        // Create a Handler that uses the Main Looper to run in
        private Handler mHandler = new Handler(Looper.getMainLooper());

        private Runnable mStatusChecker;
        private int UPDATE_INTERVAL = 2000;

        /**
         * Creates an UIUpdater object, that can be used to
         * perform UIUpdates on a specified time interval.
         * 
         * @param uiUpdater A runnable containing the update routine.
         */
        public UIUpdater(final Runnable uiUpdater) {
            mStatusChecker = new Runnable() {
                @Override
                public void run() {
                    // Run the passed runnable
                    uiUpdater.run();
                    // Re-run it after the update interval
                    mHandler.postDelayed(this, UPDATE_INTERVAL);
                }
            };
        }

        /**
         * The same as the default constructor, but specifying the
         * intended update interval.
         * 
         * @param uiUpdater A runnable containing the update routine.
         * @param interval  The interval over which the routine
         *                  should run (milliseconds).
         */
        public UIUpdater(Runnable uiUpdater, int interval){
            UPDATE_INTERVAL = interval;
            this(uiUpdater);
        }

        /**
         * Starts the periodical update routine (mStatusChecker 
         * adds the callback to the handler).
         */
        public synchronized void startUpdates(){
            mStatusChecker.run();
        }

        /**
         * Stops the periodical update routine from running,
         * by removing the callback.
         */
        public synchronized void stopUpdates(){
            mHandler.removeCallbacks(mStatusChecker);
        }
}

Затем вы можете создать объект UIUpdater внутри вашего класса и использовать его следующим образом:

...
mUIUpdater = new UIUpdater(new Runnable() {
         @Override 
         public void run() {
            // do stuff ...
         }
    });

// Start updates
mUIUpdater.startUpdates();

// Stop updates
mUIUpdater.stopUpdates();
...

Если вы хотите использовать это в качестве средства обновления активности, поместите начальный вызов в метод onResume () и остановочный вызов в onPause (), чтобы обновления начинались и останавливались в соответствии с видимостью активности.

20 голосов
/ 20 октября 2013

Я думаю, что новая горячность заключается в использовании ScheduledThreadPoolExecutor .Вот так:

private final ScheduledThreadPoolExecutor executor_ = 
        new ScheduledThreadPoolExecutor(1);
this.executor_.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
    update();
    }
}, 0L, kPeriod, kTimeUnit);
12 голосов
/ 04 февраля 2016

Таймер работает отлично. Здесь я использую таймер для поиска текста через 1,5 с и обновления пользовательского интерфейса. Надеюсь, это поможет.

private Timer _timer = new Timer();

_timer.schedule(new TimerTask() {
    @Override
    public void run() {
        // use runOnUiThread(Runnable action)
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                search();
            }
        });
    }
}, timeInterval);
5 голосов
/ 19 июня 2014

Таймер - это еще один способ сделать вашу работу, но не забудьте добавить runOnUiThread, если вы работаете с пользовательским интерфейсом.

    import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Timer;
import java.util.TimerTask;

import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.TextView;
import android.app.Activity;

public class MainActivity extends Activity {

 CheckBox optSingleShot;
 Button btnStart, btnCancel;
 TextView textCounter;

 Timer timer;
 MyTimerTask myTimerTask;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  optSingleShot = (CheckBox)findViewById(R.id.singleshot);
  btnStart = (Button)findViewById(R.id.start);
  btnCancel = (Button)findViewById(R.id.cancel);
  textCounter = (TextView)findViewById(R.id.counter);

  btnStart.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View arg0) {

    if(timer != null){
     timer.cancel();
    }

    //re-schedule timer here
    //otherwise, IllegalStateException of
    //"TimerTask is scheduled already" 
    //will be thrown
    timer = new Timer();
    myTimerTask = new MyTimerTask();

    if(optSingleShot.isChecked()){
     //singleshot delay 1000 ms
     timer.schedule(myTimerTask, 1000);
    }else{
     //delay 1000ms, repeat in 5000ms
     timer.schedule(myTimerTask, 1000, 5000);
    }
   }});

  btnCancel.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View v) {
    if (timer!=null){
     timer.cancel();
     timer = null;
    }
   }
  });

 }

 class MyTimerTask extends TimerTask {

  @Override
  public void run() {
   Calendar calendar = Calendar.getInstance();
   SimpleDateFormat simpleDateFormat = 
     new SimpleDateFormat("dd:MMMM:yyyy HH:mm:ss a");
   final String strDate = simpleDateFormat.format(calendar.getTime());

   runOnUiThread(new Runnable(){

    @Override
    public void run() {
     textCounter.setText(strDate);
    }});
  }

 }

}

и xml это ...

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:orientation="vertical"
tools:context=".MainActivity" >

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"
    android:autoLink="web"
    android:text="http://android-er.blogspot.com/"
    android:textStyle="bold" />
<CheckBox 
    android:id="@+id/singleshot"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Single Shot"/>

Другой способ использовать CountDownTimer

new CountDownTimer(30000, 1000) {

     public void onTick(long millisUntilFinished) {
         mTextField.setText("seconds remaining: " + millisUntilFinished / 1000);
     }

     public void onFinish() {
         mTextField.setText("done!");
     }
  }.start();

Запланируйте обратный отсчет до определенного времени в будущем, с регулярными уведомлениями через определенные промежутки времени. Пример отображения 30-секундного обратного отсчета в текстовом поле:

Подробнее

4 голосов
/ 10 августа 2017

Сделайте это в стиле Android с помощью Обработчик .

Объявите внутренний класс обработчика, который не пропускает память в вашемКласс Activity / Fragment

/**
     * Instances of static inner classes do not hold an implicit
     * reference to their outer class.
     */
    private static class NonLeakyHandler extends Handler {
        private final WeakReference<FlashActivity> mActivity;

        public NonLeakyHandler(FlashActivity activity) {
            mActivity = new WeakReference<FlashActivity>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            FlashActivity activity = mActivity.get();
            if (activity != null) {
                // ...
            }
        }
    }

Объявите исполняемый файл, который будет выполнять ваши повторяющиеся задачи в вашем классе Activity / Fragment

   private Runnable repeatativeTaskRunnable = new Runnable() {
        public void run() {
            new Handler(getMainLooper()).post(new Runnable() {
                @Override
                public void run() {

         //DO YOUR THINGS
        }
    };

Инициализируйте объект-обработчик в вашем Activity / Fragment (здесь FlashActivity - это мойкласс активности)

//Task Handler
private Handler taskHandler = new NonLeakyHandler(FlashActivity.this);

Повторить задачу после фиксированного временного интервала

taskHandler.postDelayed (repeatataTaskRunnable, DELAY_MILLIS);

Чтобы остановить повторение задачи

taskHandler .removeCallbacks (repeatativeTaskRunnable);

ОБНОВЛЕНИЕ: В Kotlin:

    //update interval for widget
    override val UPDATE_INTERVAL = 1000L

    //Handler to repeat update
    private val updateWidgetHandler = Handler()

    //runnable to update widget
    private var updateWidgetRunnable: Runnable = Runnable {
        run {
            //Update UI
            updateWidget()
            // Re-run it after the update interval
            updateWidgetHandler.postDelayed(updateWidgetRunnable, UPDATE_INTERVAL)
        }

    }

 // SATART updating in foreground
 override fun onResume() {
        super.onResume()
        updateWidgetHandler.postDelayed(updateWidgetRunnable, UPDATE_INTERVAL)
    }


    // REMOVE callback if app in background
    override fun onPause() {
        super.onPause()
        updateWidgetHandler.removeCallbacks(updateWidgetRunnable);
    }
4 голосов
/ 27 апреля 2016

В моем случае я должен был выполнить процесс, если одно из этих условий было выполнено: если предыдущий процесс был завершен или если уже прошло 5 секунд. Итак, я сделал следующее и работал довольно хорошо:

private Runnable mStatusChecker;
private Handler mHandler;

class {
method() {
  mStatusChecker = new Runnable() {
            int times = 0;
            @Override
            public void run() {
                if (times < 5) {
                    if (process1.isRead()) {
                        executeProcess2();
                    } else {
                        times++;
                        mHandler.postDelayed(mStatusChecker, 1000);
                    }
                } else {
                    executeProcess2();
                }
            }
        };

        mHandler = new Handler();
        startRepeatingTask();
}

    void startRepeatingTask() {
       mStatusChecker.run();
    }

    void stopRepeatingTask() {
        mHandler.removeCallbacks(mStatusChecker);
    }


}

Если процесс1 прочитан, он выполняет процесс2. Если нет, он увеличивает переменную times и заставляет обработчик выполняться через одну секунду. Он поддерживает цикл до тех пор, пока не будет прочитано process1 или пока не будет установлено значение 5. Если значение равно 5, это означает, что прошло 5 секунд, и в каждую секунду выполняется условие if процесса process1.isRead ().

4 голосов
/ 29 сентября 2015

Вы можете использовать обработчик для публикации исполняемого кода. Этот метод очень хорошо описан здесь: https://guides.codepath.com/android/Repeating-Periodic-Tasks

4 голосов
/ 05 апреля 2014

Попробуйте следующий пример, он работает !!!

Используйте [Handler] в методе onCreate (), который использует метод postDelayed (), который заставляет Runnable добавляться в очередь сообщений и запускаться послезаданное количество времени, которое составляет 0 в данном примере. 1

См. Этот код:

</p>

<pre><code>public void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
       setContentView(R.layout.main);
    //------------------
    //------------------
    android.os.Handler customHandler = new android.os.Handler();
            customHandler.postDelayed(updateTimerThread, 0);
}

private Runnable updateTimerThread = new Runnable()
{
        public void run()
        {
            //write here whaterver you want to repeat
            customHandler.postDelayed(this, 1000);
        }
};

3 голосов
/ 22 апреля 2016

Основываясь на вышеупомянутом посте относительно ScheduledThreadPoolExecutor , я предложил утилиту, которая отвечала моим потребностям (хотел запускать метод каждые 3 секунды):

class MyActivity {
    private ScheduledThreadPoolExecutor mDialogDaemon;

    private void initDebugButtons() {
        Button btnSpawnDialogs = (Button)findViewById(R.id.btn_spawn_dialogs);
        btnSpawnDialogs.setVisibility(View.VISIBLE);
        btnSpawnDialogs.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                spawnDialogs();
            }
        });
    }

    private void spawnDialogs() {
        if (mDialogDaemon != null) {
            mDialogDaemon.shutdown();
            mDialogDaemon = null;
        }
        mDialogDaemon = new ScheduledThreadPoolExecutor(1);
        // This process will execute immediately, then execute every 3 seconds.
        mDialogDaemon.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        // Do something worthwhile
                    }
                });
            }
        }, 0L, 3000L, TimeUnit.MILLISECONDS);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...