Come chiamare un metodo dopo un ritardo in Android


770

Voglio essere in grado di chiamare il seguente metodo dopo un ritardo specificato. Nell'obiettivo c c'era qualcosa del tipo:

[self performSelector:@selector(DoSomething) withObject:nil afterDelay:5];

Esiste un equivalente di questo metodo in Android con Java? Ad esempio, devo essere in grado di chiamare un metodo dopo 5 secondi.

public void DoSomething()
{
     //do something here
}

Risposte:


1859

Kotlin

Handler().postDelayed({
  //Do something after 100ms
}, 100)


Giava

final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
  @Override
  public void run() {
    //Do something after 100ms
  }
}, 100);



109
Questa soluzione è utile solo sul thread dell'interfaccia utente. Altrimenti sul thread normale, devi implementare il looper che non è la migliore versione che penso
olivier_sdg

2
@olivier_sdg perché devi implementare il looper?
Djechlin,

37
@djechlin Un gestore deve sempre essere collegato a un Looper, che elabora effettivamente il Runnable che pubblichi (). Il thread dell'interfaccia utente viene già fornito con un Looper, quindi puoi semplicemente creare un nuovo gestore () sul thread dell'interfaccia utente e pubblicare () Runnable direttamente su di esso. Questi Runnable vengono eseguiti sul thread dell'interfaccia utente. Per eseguire Runnables su un altro thread, è necessario creare un nuovo thread, quindi Looper.prepare (), creare un nuovo gestore () e quindi Looper.loop (). Tutti i Runnable inviati a questo nuovo gestore verranno eseguiti su questo nuovo thread. Se non fai tutto questo, il post () genererà un'eccezione.
Dororo

12
Nel caso sia necessario, è anche possibile annullare l'esecuzione fintanto che Runnable è ancora nella coda dei messaggi chiamando removeCallbacks(Runnable r)il Handler.
Dennis,

9
dovrebbeimport android.os.handler
KaKa,

322

Non ho potuto usare nessuna delle altre risposte nel mio caso. Ho usato invece il timer java nativo.

new Timer().schedule(new TimerTask() {          
    @Override
    public void run() {
        // this code will be executed after 2 seconds       
    }
}, 2000);

43
questo è meglio di quelli che usano Handler, perché non ha problemi di Looper quando Handler non viene eseguito sul thread dell'interfaccia utente.
Ben H,

32
Dovresti tenere un riferimento al tuo timer per annullarlo quando non è più necessario poiché secondo il documento Android: "Quando un timer non è più necessario, gli utenti dovrebbero chiamare cancel (), che rilascia il thread del timer e altre risorse. I timer non cancellati esplicitamente possono contenere risorse indefinitamente. "
Pooks,

14
Attenzione! Questo non funziona sul thread dell'interfaccia utente. L'esecuzione di questo sul thread dell'interfaccia utente ha provocato un errore irreversibile: android.view.ViewRootImpl $ CalledFromWrongThreadException: solo il thread originale che ha creato una gerarchia di viste può toccare le sue viste.
vovahost,

13
@vovahost è solo perché stai aggiornando i componenti dell'interfaccia utente all'interno del blocco timer
Tim

10
Si noti che java.util.Timer (e TimerTask) sarà deprecato in JDK 9. TimerTask crea nuovi thread per attività che non sono molto buone.
Varvara Kalinina,

183

Nota: questa risposta è stata data quando la domanda non specificava Android come contesto. Per una risposta specifica al thread dell'interfaccia utente Android, guarda qui.


Sembra che l'API di Mac OS permetta al thread corrente di continuare e pianifica l'esecuzione dell'attività in modo asincrono. In Java, la funzione equivalente è fornita dal java.util.concurrentpacchetto. Non sono sicuro di quali limiti potrebbe imporre Android.

private static final ScheduledExecutorService worker = 
  Executors.newSingleThreadScheduledExecutor();

void someMethod() {
  
  Runnable task = new Runnable() {
    public void run() {
      /* Do something… */
    }
  };
  worker.schedule(task, 5, TimeUnit.SECONDS);
  
}

3
Questo non mi chiama mai Runnable per me
Supuhstar

14
Come nota a margine: ciò consente anche di annullare l'attività in un secondo momento, il che potrebbe essere utile in alcune situazioni. Memorizza semplicemente un riferimento al ScheduledFuture<?>reso da worker.schedule()e chiama il suo cancel(boolean)metodo.
Dennis,

Penso che questa risposta sia obsoleta. .schedule non sembra più essere un metodo di Runnable ...? : /
beetree

5
@beetree è un metodo attivo ScheduledExecutorService.
Erickson,

3
Questo non funziona se sono coinvolti oggetti thread, è necessario chiamare runOnUIThread (new runnable () {run () ....}); o pubblica un runnable usando l'handler object all'interno della run () {}
Jayant Arora

107

Per eseguire qualcosa nel thread dell'interfaccia utente dopo 5 secondi:

new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
    @Override
    public void run() {
        //Do something here
    }
}, 5000);

8
Conferma, questa è la soluzione migliore per impedire la chiamata a looper.prepare e per posizionare il tutto nel thread dell'interfaccia utente.
Tobliug,

Grazie per questo, mi ha aiutato con i problemi di Looper :)
Tia

1
Starei

40

puoi usare Handler all'interno di UIThread:

runOnUiThread(new Runnable() {

    @Override
    public void run() {
         final Handler handler = new Handler();
         handler.postDelayed(new Runnable() {
           @Override
           public void run() {
               //add your code here
           }
         }, 1000);

    }
});

36

Grazie per tutte le ottime risposte, ho trovato la soluzione più adatta alle mie esigenze.

Handler myHandler = new DoSomething();
Message m = new Message();
m.obj = c;//passing a parameter here
myHandler.sendMessageDelayed(m, 1000);

class DoSomething extends Handler {
    @Override
    public void handleMessage(Message msg) {
      MyObject o = (MyObject) msg.obj;
      //do something here
    }
}

Va bene se uso questo approccio per avere feedback al tocco sul clic di un elemento .. view.setColor (some_color) e quindi rimuovere questo colore in Handler dopo x secondi ...?
eRaisedToX

25

KotlinE Javamolti modi

1. Utilizzo Handler

Handler().postDelayed({
    TODO("Do something")
    }, 2000)

2. Utilizzo di TimerTask

Timer().schedule(object : TimerTask() {
    override fun run() {
        TODO("Do something")
    }
}, 2000)

O ancora più breve

Timer().schedule(timerTask {
    TODO("Do something")
}, 2000)

O il più breve sarebbe

Timer().schedule(2000) {
    TODO("Do something")
}

3. Utilizzo Executors

Executors.newSingleThreadScheduledExecutor().schedule({
    TODO("Do something")
}, 2, TimeUnit.SECONDS)

In Java

1. Utilizzo Handler

new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        //Do something
    }
}, 2000);

2. Utilizzo Timer

new Timer().schedule(new TimerTask() {          
    @Override
    public void run() {
        // Do something
    }
}, 2000);

3. Utilizzo ScheduledExecutorService

private static final ScheduledExecutorService worker = Executors.newSingleThreadScheduledExecutor();

Runnable runnable = new Runnable() {
  public void run() {
      // Do something
  }
  };
worker.schedule(runnable, 2, TimeUnit.SECONDS);

1
@JanRabe Grazie per il tuo suggerimento. Lo apprezzo. Comunque la domanda è How to call a method after a delay in Android. Quindi mi sono concentrato su quello. Al punto. In caso contrario, le perdite di Java è un argomento importante da comprendere separatamente per gli sviluppatori.
Khemraj,

20

Vedi questa demo:

import java.util.Timer;
import java.util.TimerTask;

class Test {
     public static void main( String [] args ) {
          int delay = 5000;// in ms 

          Timer timer = new Timer();

          timer.schedule( new TimerTask(){
             public void run() { 
                 System.out.println("Wait, what..:");
              }
           }, delay);

           System.out.println("Would it run?");
     }
}

20

Se è necessario utilizzare il gestore, ma ci si trova in un altro thread, è possibile utilizzare runonuithreadper eseguire il gestore nel thread dell'interfaccia utente. Questo ti salverà da Eccezioni generate chiedendo di chiamareLooper.Prepare()

runOnUiThread(new Runnable() {
    @Override
    public void run() {
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                //Do something after 1 second
            }
        }, 1000);
    }
});

Sembra piuttosto disordinato, ma questo è uno dei modi.


4
Funziona, non riesco a modificare il tuo post a causa delle stupide regole SO con almeno 6 caratteri da modificare, ma manca '()' dopo 'nuovo gestore', dovrebbe essere 'nuovo gestore ()'
Jonathan Muller

2
Invece di inserire tutto nel thread dell'interfaccia utente, puoi fare: nuovo gestore (Looper.getMainLooper ())
Tobliug

17

Preferisco usare il View.postDelayed()metodo, codice semplice di seguito:

mView.postDelayed(new Runnable() {
    @Override
    public void run() {
        // Do something after 1000 ms
    }
}, 1000);

1
Non blocca l'elemento UI stesso, perché verrà programmato sul gestore viste?
JacksOnF1re

1
No, l'attività pubblicata verrà eseguita in 1 secondo, ma durante questo secondo thread dell'interfaccia utente fa altro lavoro utile
demaksee,

14

Ecco la mia soluzione più breve:

new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        //Do something after 100ms
    }
}, 100);

10
final Handler handler = new Handler(); 
Timer t = new Timer(); 
t.schedule(new TimerTask() { 
    public void run() { 
        handler.post(new Runnable() { 
            public void run() { 
                //DO SOME ACTIONS HERE , THIS ACTIONS WILL WILL EXECUTE AFTER 5 SECONDS...
            }
        }); 
    } 
}, 5000); 

10

Se stai utilizzando Android Studio 3.0 e versioni successive puoi utilizzare le espressioni lambda. Il metodo callMyMethod()viene chiamato dopo 2 secondi:

new Handler().postDelayed(() -> callMyMethod(), 2000);

Nel caso in cui sia necessario annullare il runnable ritardato utilizzare questo:

Handler handler = new Handler();
handler.postDelayed(() -> callMyMethod(), 2000);

// When you need to cancel all your posted runnables just use:
handler.removeCallbacksAndMessages(null);

Come possiamo cancellarlo?
Damia Fuentes,

Sono sorpreso dal fatto che molti qui si sposteranno felicemente su Kotlin, ma ignorano completamente le espressioni Lambda che sono Java standard.
TomDK,

6

Suggerisco il timer , che consente di pianificare un metodo da chiamare su un intervallo molto specifico. Ciò non bloccherà la tua interfaccia utente e manterrà la tua app reattiva durante l'esecuzione del metodo.

L'altra opzione, è wait (); metodo, questo bloccherà il thread corrente per il periodo di tempo specificato. Questo farà sì che la tua UI smetta di rispondere se lo fai sul thread dell'interfaccia utente.


2
Thread.sleep () è meglio di Object.wait (). Attendere implica che ti aspetti di essere avvisato e ti stai sincronizzando intorno ad alcune attività. Il sonno indica che desideri semplicemente non fare nulla per un determinato periodo di tempo. Il timer è la strada da percorrere se si desidera che l'azione avvenga in modo asincrono in un momento successivo.
Tim Bender,

1
Questo è vero. Ecco perché l'ho elencato come un'altra opzione ;-)
Nate,

6

Per un ritardo Post maniglia handle di linea semplice, puoi fare come segue:

new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        // Do someting
    }
}, 3000);

spero che questo possa essere d'aiuto


5

Puoi usarlo per la soluzione più semplice:

new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        //Write your code here
    }
}, 5000); //Timer is in ms here.

Altrimenti, di seguito può esserci un'altra soluzione pulita e utile:

new Handler().postDelayed(() -> 
{/*Do something here*/}, 
5000); //time in ms

5

Puoi renderlo molto più pulito usando le espressioni lambda appena introdotte:

new Handler().postDelayed(() -> {/*your code here*/}, time);

5

Quindi ci sono alcune cose da considerare qui in quanto ci sono tanti modi per scuoiare questo gatto. Sebbene le risposte siano già state tutte selezionate, selezionate e scelte. Penso che sia importante che questo venga rivisitato con linee guida di codifica adeguate per evitare che qualcuno vada nella direzione sbagliata solo a causa della "risposta semplice selezionata dalla maggioranza".

Quindi prima discutiamo la semplice risposta post ritardata che è la risposta selezionata dal vincitore in generale in questa discussione.

Un paio di cose da considerare. Dopo il ritardo post, è possibile riscontrare perdite di memoria, oggetti morti, cicli di vita che sono andati via e altro ancora. Quindi anche gestirlo correttamente è importante. Puoi farlo in un paio di modi.

Per motivi di sviluppo moderno, fornirò a KOTLIN

Ecco un semplice esempio dell'uso del thread dell'interfaccia utente su un callback e della conferma che l'attività è ancora attiva e funzionante quando si raggiunge il callback.

  Handler(Looper.getMainLooper()).postDelayed({
            if(activity != null && activity?.isFinishing == false){
                txtNewInfo.visibility = View.GONE
            }
        }, NEW_INFO_SHOW_TIMEOUT_MS)

Tuttavia, questo non è ancora perfetto in quanto non vi è alcun motivo per rispondere al callback se l'attività è andata via. quindi un modo migliore sarebbe quello di mantenere un riferimento ad esso e rimuovere i suoi callback come questo.

    private fun showFacebookStylePlus1NewsFeedOnPushReceived(){
        A35Log.v(TAG, "showFacebookStylePlus1NewsFeedOnPushReceived")
        if(activity != null && activity?.isFinishing == false){
            txtNewInfo.visibility = View.VISIBLE
            mHandler.postDelayed({
                if(activity != null && activity?.isFinishing == false){
                    txtNewInfo.visibility = View.GONE
                }
            }, NEW_INFO_SHOW_TIMEOUT_MS)
        }
    }

e ovviamente gestire la pulizia su onPause in modo che non colpisca la richiamata.

    override fun onPause() {
        super.onPause()
        mHandler.removeCallbacks(null)
    }

Ora che abbiamo discusso dell'ovvio, parliamo di un'opzione più pulita con le moderne coroutine e kotlin :). Se non li usi ancora, ti stai davvero perdendo.

   fun doActionAfterDelay() 
        launch(UI) {
            delay(MS_TO_DELAY)           
            actionToTake()
        }
    }

o se vuoi fare sempre un lancio dell'interfaccia utente con quel metodo puoi semplicemente fare:

  fun doActionAfterDelay() = launch(UI){ 
      delay(MS_TO_DELAY)           
      actionToTake()
  }

Ovviamente proprio come PostDelayed devi assicurarti di gestire l'annullamento in modo da poter fare i controlli delle attività dopo la chiamata di ritardo o puoi annullarlo in onPause proprio come l'altro percorso.

var mDelayedJob: Job? = null
fun doActionAfterDelay() 
   mDelayedJob = launch(UI) {
            try {
               delay(MS_TO_DELAY)           
               actionToTake()
            }catch(ex: JobCancellationException){
                showFancyToast("Delayed Job canceled", true, FancyToast.ERROR, "Delayed Job canceled: ${ex.message}")
            }
        }
   }
}

// gestisce la pulizia

override fun onPause() {
   super.onPause()
   if(mDelayedJob != null && mDelayedJob!!.isActive) {
      A35Log.v(mClassTag, "canceling delayed job")
      mDelayedJob?.cancel() //this should throw CancelationException in coroutine, you can catch and handle appropriately
   }
}

Se si inserisce l'avvio (UI) nella firma del metodo, il lavoro può essere assegnato nella riga del codice chiamante.

così la morale della storia è quella di essere al sicuro con le tue azioni ritardate, assicurati di rimuovere i tuoi callback, o di annullare i tuoi lavori e, naturalmente, conferma di avere il giusto ciclo di vita per toccare gli elementi sul tuo callback ritardato completo. Le Coroutine offre anche azioni annullabili.

Vale anche la pena notare che in genere è necessario gestire le varie eccezioni che possono derivare dalle coroutine. Ad esempio, una cancellazione, un'eccezione, un timeout, qualunque cosa tu decida di utilizzare. Ecco un esempio più avanzato se decidi di iniziare davvero a utilizzare le coroutine.

   mLoadJob = launch(UI){
            try {
                //Applies timeout
                withTimeout(4000) {
                    //Moves to background thread
                    withContext(DefaultDispatcher) {
                        mDeviceModelList.addArrayList(SSDBHelper.getAllDevices())
                    }
                }

                //Continues after async with context above
                showFancyToast("Loading complete", true, FancyToast.SUCCESS)
            }catch(ex: JobCancellationException){
                showFancyToast("Save canceled", true, FancyToast.ERROR, "Save canceled: ${ex.message}")
            }catch (ex: TimeoutCancellationException) {
                showFancyToast("Timed out saving, please try again or press back", true, FancyToast.ERROR, "Timed out saving to database: ${ex.message}")
            }catch(ex: Exception){
                showFancyToast("Error saving to database, please try again or press back", true, FancyToast.ERROR, "Error saving to database: ${ex.message}")
            }
        }

1
Nessun problema Rajiv, farei un ulteriore passo avanti e menzionerei che usando Live Data le coroutine possono essere consapevoli del ciclo di vita e auto-annullarsi per evitare le chiamate di pulizia, ma non voglio gettare troppe curve di apprendimento in una risposta;)
Sam,

3

Ho creato un metodo più semplice per chiamarlo.

public static void CallWithDelay(long miliseconds, final Activity activity, final String methodName)
    {
        new Handler().postDelayed(new Runnable() {

            @Override
            public void run() {
                try {
                    Method method =  activity.getClass().getMethod(methodName);
                    method.invoke(activity);
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }, miliseconds);
    }

Per usarlo, basta chiamare: .CallWithDelay(5000, this, "DoSomething");


3
Riflessione per un compito così fondamentale?
Max Ch,

Dal momento che la domanda per chiamare un metodo simile a iOS performSelector. questo è il modo migliore di fare.
HelmiB,

3

Sotto uno funziona quando ottieni,

java.lang.RuntimeException: impossibile creare un gestore all'interno del thread che non ha chiamato Looper.prepare ()

final Handler handler = new Handler(Looper.getMainLooper());
handler.postDelayed(new Runnable() {
  @Override
  public void run() {
    //Do something after 100ms
  }
}, 100);

3

Utilizzando Kotlin, possiamo ottenere facendo quanto segue

Handler().postDelayed({
    // do something after 1000ms 
}, 1000)

2

È molto facile usare il CountDownTimer. Per maggiori dettagli https://developer.android.com/reference/android/os/CountDownTimer.html

import android.os.CountDownTimer;

// calls onTick every second, finishes after 3 seconds
new CountDownTimer(3000, 1000) { 

   public void onTick(long millisUntilFinished) {
      Log.d("log", millisUntilFinished / 1000);
   }

   public void onFinish() {
      // called after count down is finished
   } 
}.start();

2

Se usi RxAndroid, la gestione dei thread e degli errori diventa molto più semplice. Il codice seguente viene eseguito dopo un ritardo

   Observable.timer(delay, TimeUnit.SECONDS)
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(aLong -> {
           // Execute code here
        }, Throwable::printStackTrace);

1

tutti sembrano dimenticare di pulire il gestore prima di pubblicare un nuovo file eseguibile o un messaggio su di esso. Altrimenti potrebbero potenzialmente accumularsi e causare comportamenti scorretti.

handler.removeMessages(int what);
// Remove any pending posts of messages with code 'what' that are in the message queue.

handler.removeCallbacks(Runnable r)
// Remove any pending posts of Runnable r that are in the message queue.

1

Ecco un altro modo complicato: non genererà eccezioni quando gli elementi dell'interfaccia utente cambiano eseguibili.

public class SimpleDelayAnimation extends Animation implements Animation.AnimationListener {

    Runnable callBack;

    public SimpleDelayAnimation(Runnable runnable, int delayTimeMilli) {
        setDuration(delayTimeMilli);
        callBack = runnable;
        setAnimationListener(this);
    }

    @Override
    public void onAnimationStart(Animation animation) {

    }

    @Override
    public void onAnimationEnd(Animation animation) {
        callBack.run();
    }

    @Override
    public void onAnimationRepeat(Animation animation) {

    }
}

Puoi chiamare l'animazione in questo modo:

view.startAnimation(new SimpleDelayAnimation(delayRunnable, 500));

L'animazione può essere collegata a qualsiasi vista.



1

Mi piacciono le cose più pulite: ecco la mia implementazione, il codice inline da usare all'interno del tuo metodo

new Handler().postDelayed(new Runnable() {
  @Override
  public void run() {
    //Do something after 100ms
  }
}, 100);

0

Una soluzione adatta in Android:

private static long SLEEP_TIME = 2 // for 2 second
.
.
MyLauncher launcher = new MyLauncher();
            launcher.start();
.
.
private class MyLauncher extends Thread {
        @Override
        /**
         * Sleep for 2 seconds as you can also change SLEEP_TIME 2 to any. 
         */
        public void run() {
            try {
                // Sleeping
                Thread.sleep(SLEEP_TIME * 1000);
            } catch (Exception e) {
                Log.e(TAG, e.getMessage());
            }
            //do something you want to do
           //And your code will be executed after 2 second
        }
    }

0

Soluzione simile ma molto più pulita da usare

Scrivi questa funzione al di fuori della classe

fun delay(duration: Long, `do`: () -> Unit) {

    Handler().postDelayed(`do`, duration)

}

Uso:

delay(5000) {
    //Do your work here
}

Cosa fa il `fare`?
Skizo-ozᴉʞS,

solo un nome, tieni tutto lì. doè un metodo integrato, quindi dobbiamo usare `per usarlo come nome variabile
Manohar Reddy

Grazie, ma perché viene utilizzato questo nome di variabile? Voglio dire qual è la sua funzione.
Skizo-ozᴉʞS

1
Ho pensato così dodopo un ritardo di 3 secondi
Manohar Reddy,

0

In Android, possiamo scrivere sotto il codice kotlin per ritardare l'esecuzione di qualsiasi funzione

class MainActivity : AppCompatActivity() {

private lateinit var handler: Handler

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    handler= Handler()
    handler.postDelayed({
        doSomething()
    },2000)
}

private fun doSomething() {
    Toast.makeText(this,"Hi! I am Toast Message",Toast.LENGTH_SHORT).show()
}
}
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.