Comment appeler une méthode après un retard dans Android

770

Je veux pouvoir appeler la méthode suivante après un délai spécifié. Dans l'objectif c, il y avait quelque chose comme:

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

Existe-t-il un équivalent de cette méthode dans Android avec Java? Par exemple, je dois pouvoir appeler une méthode après 5 secondes.

public void DoSomething()
{
     //do something here
}
aryaxt
la source

Réponses:

1860

Kotlin

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


Java

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


continuité
la source
109
Cette solution est utile uniquement sur le thread d'interface utilisateur. Sinon sur un thread normal, vous devez implémenter looper qui n'est pas la meilleure version je pense
olivier_sdg
2
@olivier_sdg pourquoi avez-vous besoin d'implémenter le looper?
djechlin
37
@djechlin Un gestionnaire doit toujours être lié à un Looper, qui traitera en fait le Runnable que vous publiez (). Le thread d'interface utilisateur est déjà livré avec un Looper, vous pouvez donc simplement créer un nouveau gestionnaire () sur le thread d'interface utilisateur et y exécuter directement des runnables. Ces Runnables s'exécutent sur le thread d'interface utilisateur. Pour que Runnables s'exécute sur un autre thread, vous devez créer un nouveau thread, puis Looper.prepare (), créer un nouveau gestionnaire () puis Looper.loop (). Tout Runnables publié sur ce nouveau gestionnaire s'exécutera sur ce nouveau thread. Si vous ne faites pas tout cela, le post () lèvera une exception.
Dororo
12
Si vous en avez besoin, vous pouvez également annuler l'exécution tant que Runnable est toujours dans la file d'attente de messages en appelant removeCallbacks(Runnable r)le Handler.
Dennis
9
devraitimport android.os.handler
KaKa
322

Je n'ai pu utiliser aucune des autres réponses dans mon cas. J'ai plutôt utilisé le minuteur Java natif.

new Timer().schedule(new TimerTask() {          
    @Override
    public void run() {
        // this code will be executed after 2 seconds       
    }
}, 2000);
Jules Colle
la source
43
c'est mieux que ceux qui utilisent le gestionnaire, car il n'a pas de problèmes de zone répétée lorsque le gestionnaire n'est pas exécuté sur le thread d'interface utilisateur.
Ben H
32
Vous devez conserver une référence à votre minuteur afin de l'annuler lorsqu'il n'est plus nécessaire car selon le document Android: "Quand un minuteur n'est plus nécessaire, les utilisateurs doivent appeler cancel (), ce qui libère le fil du minuteur et d'autres ressources. Les temporisateurs non explicitement annulés peuvent contenir des ressources indéfiniment. "
Pooks
14
Attention! Cela ne fonctionne pas sur le thread d'interface utilisateur. L'exécution de ceci sur le thread d'interface utilisateur a provoqué une erreur fatale: android.view.ViewRootImpl $ CalledFromWrongThreadException: seul le thread d'origine qui a créé une hiérarchie de vues peut toucher ses vues.
vovahost
13
@vovahost, c'est uniquement parce que vous mettez à jour les composants de l'interface utilisateur à l'intérieur du bloc de minuterie
Tim
10
Notez que java.util.Timer (et TimerTask) seront obsolètes dans JDK 9. TimerTask crée de nouveaux Threads pour les tâches, ce qui n'est pas très bon.
Varvara Kalinina
183

Remarque: Cette réponse a été donnée lorsque la question ne spécifiait pas Android comme contexte. Pour une réponse spécifique au fil d'AU Android, regardez ici.


Il semble que l'API Mac OS laisse le thread actuel continuer et planifie l'exécution de la tâche de manière asynchrone. En Java, la fonction équivalente est fournie par le java.util.concurrentpackage. Je ne sais pas quelles limitations Android pourrait imposer.

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

void someMethod() {
  
  Runnable task = new Runnable() {
    public void run() {
      /* Do something… */
    }
  };
  worker.schedule(task, 5, TimeUnit.SECONDS);
  
}
erickson
la source
3
Cela n'appelle jamais le Runnable pour moi
Supuhstar
14
Remarque: cela vous permet également d' annuler la tâche ultérieurement, ce qui peut être utile dans certaines situations. Enregistrez simplement une référence au ScheduledFuture<?>retourné par worker.schedule()et appelez sa cancel(boolean)méthode.
Dennis
Je pense que cette réponse est dépassée. .schedule ne semble plus être une méthode de Runnable ...? : /
beetree
5
@beetree c'est une méthode ScheduledExecutorService.
erickson
3
Cela ne fonctionne pas si des objets de thread ui sont impliqués, vous devez appeler runOnUIThread (new runnable () {run () ....}); ou publier un exécutable à l'aide d'un objet gestionnaire depuis l'intérieur du run () {}
Jayant Arora
107

Pour exécuter quelque chose dans le thread d'interface utilisateur après 5 secondes:

new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
    @Override
    public void run() {
        //Do something here
    }
}, 5000);
bombardier
la source
8
Confirmez, c'est la meilleure solution pour empêcher l'appel à looper.prepare et pour placer le tout dans le thread d'interface utilisateur.
Tobliug
Merci pour cela, m'a aidé avec les problèmes de Looper :)
Tia
1
Je ferais attention à ne pas créer de gestionnaire sur le boucleur principal, alors dans ce fil, aucune tâche de longue durée ne devrait être effectuée
Shayan_Aryan
40

vous pouvez utiliser Handler dans 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);

    }
});
Hossam Ghareeb
la source
36

Merci pour toutes les bonnes réponses, j'ai trouvé une solution qui correspond le mieux à mes besoins.

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
    }
}
aryaxt
la source
Est-ce correct si j'utilise cette approche pour avoir un retour tactile sur le clic d'un élément .. view.setColor (some_color) puis supprimer cette couleur dans le gestionnaire après x secondes ...?
eRaisedToX
25

KotlinEt de Javanombreuses façons

1. Utilisation Handler

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

2. Utilisation de TimerTask

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

Ou encore plus court

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

Ou le plus court serait

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

3. Utilisation Executors

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

En Java

1. Utilisation Handler

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

2. Utilisation Timer

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

3. Utilisation ScheduledExecutorService

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

Runnable runnable = new Runnable() {
  public void run() {
      // Do something
  }
  };
worker.schedule(runnable, 2, TimeUnit.SECONDS);
Khemraj
la source
1
@JanRabe Merci pour votre suggestion. Je l'apprécie. Mais la question est How to call a method after a delay in Android. Je me suis donc concentré sur cela. Jusqu'au point. Sinon, les fuites Java sont un gros sujet à comprendre séparément pour les développeurs.
Khemraj
20

Voir cette démo:

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?");
     }
}
OscarRyz
la source
20

Si vous devez utiliser le gestionnaire, mais que vous êtes dans un autre thread, vous pouvez utiliser runonuithreadpour exécuter le gestionnaire dans le thread d'interface utilisateur. Cela vous évitera des exceptions levées demandant d'appelerLooper.Prepare()

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

Semble assez désordonné, mais c'est l'un des moyens.

Noob
la source
4
Cela fonctionne, je ne peux pas modifier votre message en raison de règles stupides de SO avec au moins 6 caractères à modifier, mais il y a '()' manquant après 'nouveau gestionnaire' il devrait être 'nouveau gestionnaire ()'
Jonathan Muller
2
Au lieu de tout placer dans le thread d'interface utilisateur, vous pouvez faire: nouveau gestionnaire (Looper.getMainLooper ())
Tobliug
17

Je préfère utiliser la View.postDelayed()méthode, le code simple ci-dessous:

mView.postDelayed(new Runnable() {
    @Override
    public void run() {
        // Do something after 1000 ms
    }
}, 1000);
codezjx
la source
1
Ne gèle-t-il pas l'élément ui lui-même, car il sera planifié sur le gestionnaire de vues?
JacksOnF1re
1
Non, la tâche publiée sera exécutée en 1 seconde, mais pendant ce deuxième thread d'interface utilisateur fait un autre travail utile
demaksee
14

Voici ma solution la plus courte:

new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        //Do something after 100ms
    }
}, 100);
Alecs
la source
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); 
Vishnu
la source
10

Si vous utilisez Android Studio 3.0 et supérieur, vous pouvez utiliser des expressions lambda. La méthode callMyMethod()est appelée après 2 secondes:

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

Au cas où vous auriez besoin d'annuler le runnable retardé, utilisez ceci:

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

// When you need to cancel all your posted runnables just use:
handler.removeCallbacksAndMessages(null);
vovahost
la source
Comment pouvons-nous annuler cela?
Damia Fuentes
Je suis surpris du nombre de personnes qui passeront volontiers à Kotlin, tout en ignorant complètement les expressions Lambda qui sont du Java standard.
TomDK
6

Je suggère la minuterie , il vous permet de programmer une méthode à appeler sur un intervalle très précis. Cela ne bloquera pas votre interface utilisateur et gardera votre application sensible pendant l'exécution de la méthode.

L'autre option est l' attente (); , cela bloquera le thread en cours pendant la durée spécifiée. Cela entraînera votre interface utilisateur cesser de répondre si vous le faites sur le thread d'interface utilisateur.

Nate
la source
2
Thread.sleep () est meilleur que Object.wait (). Attendre signifie que vous vous attendez à être averti et que vous vous synchronisez autour d'une activité. Le sommeil indique que vous souhaitez simplement ne rien faire pendant un certain temps. La minuterie est le chemin à parcourir si vous souhaitez que l'action se produise de manière asynchrone à un moment ultérieur.
Tim Bender
1
C'est vrai. C'est pourquoi je l'ai listé comme une autre option ;-)
Nate
6

Pour un délai de traitement de ligne simple, vous pouvez procéder comme suit:

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

J'espère que ça aide

MrG
la source
5

Vous pouvez l'utiliser pour la solution la plus simple:

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

Sinon, ci-dessous peut être une autre solution utile propre:

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

Vous pouvez le rendre beaucoup plus propre en utilisant les expressions lambda nouvellement introduites:

new Handler().postDelayed(() -> {/*your code here*/}, time);
Alnour Alharin
la source
5

Il y a donc quelques éléments à considérer ici car il y a tellement de façons de dépouiller ce chat. Bien que toutes les réponses aient déjà été données sélectionnées et choisies. Je pense qu'il est important que cela soit revisité avec des directives de codage appropriées pour éviter que quiconque aille dans la mauvaise direction simplement à cause de la "réponse simple choisie par la majorité".

Alors d'abord, discutons de la réponse simple Post Delayed qui est la réponse sélectionnée par le gagnant dans l'ensemble de ce fil.

Quelques points à considérer. Après le délai de post-traitement, vous pouvez rencontrer des fuites de mémoire, des objets morts, des cycles de vie qui ont disparu, etc. Il est donc également important de le manipuler correctement. Vous pouvez le faire de deux manières.

Par souci de développement moderne, je fournirai en KOTLIN

Voici un exemple simple d'utilisation du thread d'interface utilisateur sur un rappel et de confirmation que votre activité est toujours bien vivante lorsque vous appuyez sur votre rappel.

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

Cependant, ce n'est toujours pas parfait car il n'y a aucune raison de frapper votre rappel si l'activité a disparu. donc une meilleure façon serait de garder une référence et de supprimer ses rappels comme celui-ci.

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

et bien sûr gérer le nettoyage sur le onPause afin qu'il ne frappe pas le rappel.

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

Maintenant que nous avons parlé de l'évidence, parlons d'une option plus propre avec les coroutines modernes et la kotlin :). Si vous ne les utilisez pas encore, vous en manquez vraiment.

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

ou si vous voulez toujours faire un lancement d'interface utilisateur sur cette méthode, vous pouvez simplement faire:

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

Bien sûr, tout comme le PostDelayed, vous devez vous assurer de gérer l'annulation afin de pouvoir effectuer les vérifications d'activité après l'appel différé ou vous pouvez l'annuler dans la onPause, tout comme l'autre itinéraire.

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

// gérer le nettoyage

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

Si vous placez le lancement (UI) dans la signature de méthode, le travail peut être attribué dans la ligne de code appelante.

La morale de l'histoire est donc d'être en sécurité avec vos actions retardées, assurez-vous de supprimer vos rappels ou d'annuler vos travaux et bien sûr de confirmer que vous avez le bon cycle de vie pour toucher les éléments de votre rappel différé. Les Coroutines proposent également des actions annulables.

Il convient également de noter que vous devez généralement gérer les diverses exceptions qui peuvent accompagner les coroutines. Par exemple, une annulation, une exception, un délai d'expiration, tout ce que vous décidez d'utiliser. Voici un exemple plus avancé si vous décidez de vraiment commencer à utiliser des coroutines.

   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}")
            }
        }
Sam
la source
1
Pas de problème Rajiv, je voudrais aller plus loin et mentionner que l'utilisation de Live Data les coroutines peuvent être conscientes du cycle de vie et s'annuler automatiquement pour éviter les appels de nettoyage, mais je ne veux pas lancer trop de courbes d'apprentissage dans une seule réponse;)
Sam
3

J'ai créé une méthode plus simple pour appeler cela.

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

Pour l'utiliser, il suffit d'appeler: .CallWithDelay(5000, this, "DoSomething");

HelmiB
la source
3
Réflexion pour une tâche aussi fondamentale?
Max Ch
Depuis la question à appeler méthode similaire à iOS performSelector. c'est la meilleure façon de procéder.
HelmiB
3

Ci-dessous on travaille quand vous obtenez,

java.lang.RuntimeException: impossible de créer un gestionnaire dans le thread qui n'a pas appelé Looper.prepare ()

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

En utilisant Kotlin, nous pouvons y parvenir en procédant comme suit

Handler().postDelayed({
    // do something after 1000ms 
}, 1000)
Nikhil Katekhaye
la source
2

C'est très simple d'utiliser le CountDownTimer. Pour plus de détails 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();
Abdul Rizwan
la source
2

Si vous utilisez RxAndroid, la gestion des threads et des erreurs devient beaucoup plus facile. Le code suivant s'exécute après un délai

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

tout le monde semble oublier de nettoyer le gestionnaire avant de poster un nouveau runnable ou un message dessus. Sinon, ils pourraient potentiellement s'accumuler et provoquer un mauvais comportement.

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.
Dan Alboteanu
la source
1

Voici un autre moyen délicat: il ne lèvera pas d'exception lorsque le runnable change les éléments de l'interface utilisateur.

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

    }
}

Vous pouvez appeler l'animation comme ceci:

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

L'animation peut être attachée à n'importe quelle vue.

cdytoby
la source
1

Voici la réponse dans Kotlin vous les gens paresseux, paresseux:

Handler().postDelayed({
//doSomethingHere()
}, 1000)
Daniel Wilson
la source
1

J'aime les choses plus propres: voici mon implémentation, du code en ligne à utiliser dans votre méthode

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

Une solution adaptée dans 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
        }
    }
Faakhir
la source
0

Solution similaire mais beaucoup plus propre à utiliser

Écrivez cette fonction en dehors de la classe

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

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

}

Usage:

delay(5000) {
    //Do your work here
}
Manohar Reddy
la source
Que fait le «faire»?
Skizo-ozᴉʞS
juste un nom, gardez n'importe quoi. doest une méthode intégrée, nous devons donc utiliser `pour l'utiliser comme nom de variable
Manohar Reddy
Merci, mais pourquoi ce nom de variable est-il utilisé? Je veux dire quelle est sa fonction.
Skizo-ozᴉʞS
1
Je pensais comme doça après un retard de 3 sec
Manohar Reddy
0

Sous Android, nous pouvons écrire ci-dessous du code kotlin pour retarder l'exécution de n'importe quelle fonction

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()
}
}
Ahasan
la source