Ripetere un'attività con un ritardo?


216

Ho una variabile nel mio codice che dice che è "status".

Voglio visualizzare del testo nell'applicazione in base a questo valore variabile. Questo deve essere fatto con un ritardo specifico.

È come,

  • Controlla il valore della variabile di stato

  • Mostra del testo

  • Attendi 10 secondi

  • Controlla il valore della variabile di stato

  • Mostra del testo

  • Attendi 15 secondi

e così via. Il ritardo può variare e viene impostato una volta visualizzato il testo.

Ho provato Thread.sleep(time delay)e non è riuscito. Qual è il modo migliore per farlo?


Risposte:


448

Dovresti usare Handlerla postDelayedfunzione per questo scopo. Eseguirà il tuo codice con il ritardo specificato sul thread dell'interfaccia utente principale, quindi sarai in grado di aggiornare i controlli dell'interfaccia utente.

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

1
Grazie inazaruk, sono riuscito a farlo funzionare. Ho trovato 2 v piccoli errori di battitura (in alto il suo "Handler" non "Handle" e in fondo il suo "removeCallbacks" non rimuove "removecallback". Ma in ogni caso il codice era esattamente quello che stavo cercando. fare per restituire il favore. Per lo meno hai vinto il mio rispetto. Cordiali saluti Aubrey Bourke.
Aubreybourke,

20
Bel programma, funziona assolutamente bene. Ma startRepeatingTask () doveva essere chiamato dal metodo onCreate / thread dell'interfaccia utente (mi ci è voluto del tempo per realizzarlo!), Forse questo punto avrebbe potuto essere menzionato da qualche parte. Saluti
gkris,

1
la tua risposta continua a dare. Questo mi ha aiutato a uscire da un buco oggi. Grazie.
Dean Blakely,

Esiste un modo per avere un Runnable ripetuto all'interno del metodo getView () di un adattatore?
toobsco42,

1
Qui quando importiamo le classi cosa dovremmo importare? android.os.Handler o java.util.logging.Handler?
EJ Chathuranga,

34

Per chiunque sia interessato, ecco una classe che ho creato usando il codice di inazaruk che crea tutto il necessario (l'ho chiamato UIUpdater perché lo uso per aggiornare periodicamente l'interfaccia utente, ma puoi chiamarlo come preferisci):

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

È quindi possibile creare un oggetto UIUpdater all'interno della classe e utilizzarlo in questo modo:

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

// Start updates
mUIUpdater.startUpdates();

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

Se si desidera utilizzarlo come programma di aggiornamento delle attività, inserire la chiamata di avvio all'interno del metodo onResume () e la chiamata di arresto all'interno di onPause (), in modo che gli aggiornamenti vengano avviati e arrestati in base alla visibilità dell'attività.


1
Modificato: UPDATE_INTERVAL = interval;dovrebbe essere prima di this(uiUpdater); in UIUpdater(Runnable uiUpdater, int interval)(poiché UPDATE_INTERVALviene utilizzato il valore di e dovrebbe essere quello passato come parametro interval;). Se possibile, evita anche la larghezza di oltre 80 caratteri nel codice (quasi sempre;)
Mr_and_Mrs_D

5
Questa classe presenta una serie di problemi. In primo luogo, dovrebbe essere istanziato nel thread principale per poter aggiornare la GUI. Si potrebbe avere risolto questo passando il crochet principale al costruttore gestore: new Handler(Looper.getMainLooper()). In secondo luogo, non convalida gli argomenti, quindi ingoia valori eseguibili null e intervalli negativi. Infine, non tiene conto del tempo trascorso nella uiUpdater.run()linea, né gestisce le possibili eccezioni generate da tale metodo. Inoltre non è thread-safe, dovresti creare starte stopmetodi sincronizzati.
Mister Smith,

2
Modificato fino alla parte di convalida dell'argomento, poiché non ho Eclipse qui per testare il codice. Grazie per il feedback! È questo che intendevi? StartUpdates e stopUpdates sincronizzati e inserisci una chiamata Looper.getMainLooper () all'interno del dispositivo di controllo Handler (spero che tu possa chiamarlo direttamente dalla dichiarazione sul campo)
ravemir

2
Capisco: error: call to this must be first statement in constructorforse c'è una soluzione semplice.
msysmilu,

4
Upgrade per avere importazione - richiede tempo per capire da dove viene Handler quando si programma in modo casuale in Java
Roman Susi,

23

Penso che il nuovo entusiasmo sia usare un ScheduledThreadPoolExecutor . Così:

private final ScheduledThreadPoolExecutor executor_ = 
        new ScheduledThreadPoolExecutor(1);
this.executor_.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
    update();
    }
}, 0L, kPeriod, kTimeUnit);

Executors.newSingleThreadScheduledExecutor()può essere un'altra opzione qui.
Gulshan,

13

Il timer funziona bene. Qui, utilizzo Timer per cercare testo dopo 1,5 secondi e aggiornare l'interfaccia utente. Spero che aiuti.

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);

dove hai messo l'intervallo di tempo?
Nathiel Barros,

1
Ciao Nathiel, ho appena aggiornato il mio post, spero che sia di aiuto! Il tempo di intervallo è il secondo parametro di Timer.schedule ().
Kai Wang,

7

Ci sono 3 modi per farlo:

Utilizzare ScheduledThreadPoolExecutor

Un po 'eccessivo poiché non è necessario un pool di Thread

   //----------------------SCHEDULER-------------------------
    private final ScheduledThreadPoolExecutor executor_ =
            new ScheduledThreadPoolExecutor(1);
     ScheduledFuture<?> schedulerFuture;
   public void  startScheduler() {
       schedulerFuture=  executor_.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                //DO YOUR THINGS
                pageIndexSwitcher.setVisibility(View.GONE);
            }
        }, 0L, 5*MILLI_SEC,  TimeUnit.MILLISECONDS);
    }


    public void  stopScheduler() {
        pageIndexSwitcher.setVisibility(View.VISIBLE);
        schedulerFuture.cancel(false);
        startScheduler();
    }

Usa attività timer

Vecchio stile Android

    //----------------------TIMER  TASK-------------------------

    private Timer carousalTimer;
    private void startTimer() {
        carousalTimer = new Timer(); // At this line a new Thread will be created
        carousalTimer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                //DO YOUR THINGS
                pageIndexSwitcher.setVisibility(INVISIBLE);
            }
        }, 0, 5 * MILLI_SEC); // delay
    }

    void stopTimer() {
        carousalTimer.cancel();
    }

Usa gestore e eseguibile

Stile Android moderno

    //----------------------HANDLER-------------------------

    private Handler taskHandler = new android.os.Handler();

    private Runnable repeatativeTaskRunnable = new Runnable() {
        public void run() {
            //DO YOUR THINGS
        }
    };

   void startHandler() {
        taskHandler.postDelayed(repeatativeTaskRunnable, 5 * MILLI_SEC);
    }

    void stopHandler() {
        taskHandler.removeCallbacks(repeatativeTaskRunnable);
    }

Gestore non perdente con attività / contesto

Dichiara una classe Handler interna che non perde memoria nella tua classe 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) {
                // ...
            }
        }
    }

Dichiarare un eseguibile che eseguirà l'attività ripetitiva nella classe Attività / Frammento

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

         //DO YOUR THINGS
        }
    };

Inizializza l'oggetto Gestore nella tua attività / frammento (qui FlashActivity è la mia classe di attività)

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

Per ripetere un'attività dopo l'intervallo di tempo fisso

taskHandler.postDelayed (repeatativeTaskRunnable, DELAY_MILLIS);

Per interrompere la ripetizione dell'attività

taskHandler .removeCallbacks (repeatativeTaskRunnable);

AGGIORNAMENTO: a 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);
    }

6

Il timer è un altro modo di fare il tuo lavoro, ma sii tranquillo di aggiungere runOnUiThreadse stai lavorando con l'interfaccia utente.

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

 }

}

e 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"/>

Un altro modo di usare CountDownTimer

new CountDownTimer(30000, 1000) {

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

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

Pianifica un conto alla rovescia fino a un momento futuro, con notifiche regolari a intervalli lungo il percorso. Esempio di visualizzazione di un conto alla rovescia di 30 secondi in un campo di testo:

Per dettagli


1
Il gestore è preferito rispetto al timer. Vedi Timer vs Handler
Suragch

4

Prova a seguire l'esempio funziona !!!

Utilizzare [Handler] nel metodo onCreate () che utilizza il metodo postDelayed () che fa sì che il Runnable venga aggiunto alla coda dei messaggi, da eseguire dopo il periodo di tempo specificato che è 0 in un dato esempio. 1

Segnala questo codice:

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



4

Sulla base del post precedente relativo a ScheduledThreadPoolExecutor , ho trovato un'utilità adatta alle mie esigenze (volevo attivare un metodo ogni 3 secondi):

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

4

Nel mio caso, ho dovuto eseguire un processo se una di queste condizioni fosse vera: se un processo precedente era stato completato o se erano già passati 5 secondi. Quindi, ho fatto quanto segue e ho lavorato abbastanza bene:

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


}

Se viene letto process1, esegue process2. In caso contrario, incrementa i tempi variabili e fa eseguire il gestore dopo un secondo. Mantiene un ciclo finché non viene letto process1 o i tempi sono 5. Quando times è 5, significa che sono trascorsi 5 secondi e in ogni secondo viene eseguita la clausola if di process1.isRead ().


1

Utilizzando kotlin e la sua Coroutine è abbastanza semplice, prima proclama un lavoro nella tua classe (meglio nella tua vistaModello) in questo modo:

private var repeatableJob: Job? = null

quindi quando si desidera creare e avviarlo, procedere come segue:

repeatableJob = viewModelScope.launch {
    while (isActive) {
         delay(5_000)
         loadAlbums(iImageAPI, titleHeader, true)
    }
}
repeatableJob?.start()

e se vuoi finirlo:

repeatableJob?.cancel()

PS: viewModelScopeè disponibile solo nei modelli di visualizzazione, è possibile utilizzare altri ambiti Coroutine comewithContext(Dispatchers.IO)

Ulteriori informazioni: qui


0

Per le persone che usano Kotlin, la risposta di inazaruk non funzionerà, l'IDE richiederà l'inizializzazione della variabile, quindi invece di usare l' postDelayedinterno Runnable, la useremo in un metodo separato.

  • Inizializza il tuo in Runnablequesto modo:

    private var myRunnable = Runnable {
        //Do some work
        //Magic happens here ↓
        runDelayedHandler(1000)   }
  • Inizializza il tuo runDelayedHandlermetodo in questo modo:

     private fun runDelayedHandler(timeToWait : Long) {
        if (!keepRunning) {
            //Stop your handler
            handler.removeCallbacksAndMessages(null)
            //Do something here, this acts like onHandlerStop
        }
        else {
            //Keep it running
            handler.postDelayed(myRunnable, timeToWait)
        }
    }
  • Come puoi vedere, questo approccio ti consentirà di controllare la durata dell'attività, tenere traccia keepRunninge modificarla durante la vita dell'applicazione farà il lavoro per te.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.