Como chamar um método após um atraso no Android


770

Quero poder chamar o método a seguir após um atraso especificado. No objetivo c, havia algo como:

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

Existe um equivalente desse método no android com java? Por exemplo, eu preciso poder chamar um método após 5 segundos.

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

Respostas:


1859

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



109
Esta solução é útil apenas no thread da interface do usuário. Caso contrário, no encadeamento normal, você precisará implementar o looper, que não é a melhor versão que eu acho.
olivier_sdg

2
@olivier_sdg Por que você precisa implementar o looper?
djechlin

37
@djechlin Um manipulador deve sempre estar vinculado a um looper, que realmente processa o executável que você publica (). O thread da interface do usuário já vem com um Looper, para que você possa criar um novo Handler () no thread da UI e postar () Runnables diretamente nele. Esses Runnables são executados no thread da interface do usuário. Para que o Runnables seja executado em outro thread, você precisa criar um novo thread, Looper.prepare (), criar um novo Handler () e Looper.loop (). Quaisquer Runnables postados neste novo manipulador serão executados nesse novo thread. Se você não fizer tudo isso, o post () lançará uma exceção.
Dororo

12
Caso seja necessário, você também pode cancelar a execução enquanto o Runnable ainda estiver na fila de mensagens, chamando removeCallbacks(Runnable r)o Handler.
Dennis

9
deveriaimport android.os.handler
KaKa 16/08

322

Eu não poderia usar nenhuma das outras respostas no meu caso. Eu usei o java Timer nativo.

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

43
isso é melhor do que os que usam o manipulador, porque não tem problemas com o Looper quando o manipulador não é executado no thread da interface do usuário.
Ben H

32
Você deve manter uma referência ao seu cronômetro para cancelá-lo quando não for mais necessário, pois de acordo com o documento do Android: "Quando um cronômetro não é mais necessário, os usuários devem chamar cancel (), que libera o encadeamento do cronômetro e outros recursos. Temporizadores não explicitamente cancelados podem reter recursos indefinidamente. "
Pooks

14
Atenção! Isso não é executado no thread da interface do usuário. A execução disso no encadeamento da interface do usuário causou um Erro Fatal: android.view.ViewRootImpl $ CalledFromWrongThreadException: Somente o encadeamento original que criou uma hierarquia de exibição pode tocar em suas exibições.
vovahost

13
@vovahost isso é só porque você está atualizando componentes de interface do usuário dentro do bloco temporizador
Tim

10
Observe que java.util.Timer (e TimerTask) serão preteridos no JDK 9. O TimerTask cria novos Threads para tarefas que não são muito boas.
Varvara Kalinina

183

Nota: Essa resposta foi dada quando a pergunta não especificou o Android como contexto. Para obter uma resposta específica ao segmento da interface do usuário do Android, consulte aqui.


Parece que a API do Mac OS permite que o encadeamento atual continue e agende a execução da tarefa de forma assíncrona. No Java, a função equivalente é fornecida pelo java.util.concurrentpacote. Não sei ao certo quais limitações o Android pode impor.

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
Isso nunca chama o Runnable para mim #
Supuhstar 17/03/2014

14
Como observação lateral: Isso também permite que você cancele a tarefa posteriormente, o que pode ser útil em algumas situações. Simplesmente armazene uma referência ao ScheduledFuture<?>retornado por worker.schedule()e chame seu cancel(boolean)método.
Dennis

Eu acho que essa resposta está desatualizada. .schedule não parece mais ser um método de Runnable ...? : /
beetree

5
@ beetree é um método ScheduledExecutorService.
quer

3
Isso não funciona se houver objetos de thread da interface do usuário, você deve chamar runOnUIThread (new runnable () {run () ....}); ou deixar um objecto usando manipulador executável a partir de dentro do prazo () {}
Jayant Arora

107

Para executar algo no thread da interface do usuário após 5 segundos:

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

8
Confirme, esta é a melhor solução para impedir a chamada de looper.prepare e colocar tudo no thread da interface do usuário.
18715 Tobliug

Obrigado por isso, me ajudou com questões Looper :)
Tia

1
Gostaria de ter cuidado sobre como criar um manipulador em looper principal, então neste segmento tarefa de longa data não deve ser feito
Shayan_Aryan

40

você pode usar o Handler dentro do 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

Obrigado por todas as ótimas respostas, encontrei uma solução que melhor se adapta às minhas necessidades.

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

Tudo bem se eu usar essa abordagem para receber feedback por toque ao clicar em um item. View.setColor (some_color) e depois remover essa cor no Handler após x segundos ...?
eRaisedToX

25

Kotlin& JavaMuitas maneiras

1. Usando Handler

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

2. Usando o TimerTask

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

Ou ainda mais curto

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

Ou o menor seria

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

3. Usando Executors

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

Em Java

1. Usando Handler

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

2. Usando Timer

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

3. Usando 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 Obrigado pela sua sugestão. Eu apriciado isso. No entanto, a pergunta é How to call a method after a delay in Android. Então, eu me concentrei nisso. Ao ponto. Caso contrário, o vazamento de java é um grande tópico a ser entendido separadamente pelos desenvolvedores.
Khemraj

20

Veja esta demonstração:

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 você precisar usar o manipulador, mas estiver em outro thread, poderá usá-lo runonuithreadpara executar o manipulador no thread da interface do usuário. Isso o salvará das exceções lançadas pedindo para ligarLooper.Prepare()

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

Parece bastante confuso, mas esse é um dos caminhos.


4
Isso funciona, não consigo editar sua postagem por causa de regras estúpidas de SO com no mínimo 6 caracteres para editar, mas há '()' ausente após 'new Handler', deve ser 'new Handler ()'
Jonathan Muller

2
Em vez de colocar tudo no segmento interface do usuário, você pode fazer: nova Handler (Looper.getMainLooper ())
Tobliug

17

Eu prefiro usar o View.postDelayed()método, código simples abaixo:

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

1
Ele não congela o próprio elemento da interface do usuário, porque será agendado no manipulador de visualizações?
JacksOnF1re

1
Não, tarefa publicada será executado em 1 segundo, mas durante este segundo segmento interface do usuário faz outro trabalho útil
demaksee

14

Aqui está a minha solução mais curta:

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 você estiver usando o Android Studio 3.0 e superior, poderá usar expressões lambda. O método callMyMethod()é chamado após 2 segundos:

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

Caso você precise cancelar a execução atrasada, use o seguinte:

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

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

Como podemos cancelar isso?
Damia Fuentes 26/09

Estou surpreso quantos aqui passarão felizes para o Kotlin, mas ignorarão completamente as Expressões Lambda que são Java padrão.
TomDK 16/08/19

6

Sugiro o Timer , que permite agendar um método a ser chamado em um intervalo muito específico. Isso não bloqueará sua interface do usuário e manterá seu aplicativo sensível enquanto o método estiver sendo executado.

A outra opção é a wait (); método, isso bloqueará o encadeamento atual pelo período especificado. Isso fará com que sua interface do usuário pare de responder se você fizer isso no thread da interface do usuário.


2
Thread.sleep () é melhor que Object.wait (). A espera implica que você espera ser notificado e está sincronizando em torno de alguma atividade. O sono indica que você simplesmente deseja não fazer nada por um tempo especificado. O cronômetro é o caminho a percorrer, se você deseja que a ação ocorra de forma assíncrona em algum momento posterior.
Tim Bender

1
Isso é verdade. É por isso que eu listados como mais uma opção ;-)
Nate

6

Para um atraso de manipulação de linha simples, você pode fazer o seguinte:

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

Eu espero que isso ajude


5

Você pode usar isso para a solução mais simples:

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

Senão, Abaixo pode ser outra solução útil limpa:

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

5

Você pode torná-lo muito mais limpo usando as expressões lambda recém-introduzidas:

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

5

Portanto, existem algumas coisas a considerar aqui, pois existem muitas maneiras de esfolar esse gato. Embora todas as respostas já tenham sido dadas, foram selecionadas e escolhidas. Eu acho que é importante que isso seja revisado com diretrizes de codificação adequadas para evitar que alguém vá na direção errada apenas por causa da "resposta simples selecionada pela maioria".

Então, primeiro vamos discutir a resposta simples pós-adiada que é a resposta selecionada pelo vencedor em geral neste tópico.

Algumas coisas a considerar. Após o atraso da postagem, você poderá encontrar vazamentos de memória, objetos mortos, ciclos de vida que desapareceram e muito mais. Portanto, lidar com isso corretamente também é importante. Você pode fazer isso de duas maneiras.

Por uma questão de desenvolvimento moderno, fornecerei em KOTLIN

Aqui está um exemplo simples de como usar o encadeamento da interface do usuário em um retorno de chamada e confirmar que sua atividade ainda está ativa e bem quando você pressiona seu retorno de chamada.

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

No entanto, isso ainda não é perfeito, pois não há motivo para acessar sua chamada de retorno se a atividade desaparecer. portanto, uma maneira melhor seria manter uma referência a ele e remover seus retornos de chamada assim.

    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, é claro, lida com a limpeza na onPause, para que não atinja o retorno de chamada.

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

Agora que falamos do óbvio, vamos falar sobre uma opção mais limpa com as corotinas dos dias modernos e o kotlin :). Se você ainda não os está usando, está realmente perdendo.

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

ou se você quiser sempre executar uma interface do usuário nesse método, basta:

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

É claro que, assim como o PostDelayed, você deve garantir o cancelamento, para que você possa realizar as verificações de atividade após a chamada de atraso ou cancelá-la na onPause, como na outra rota.

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

// manipular limpeza

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 você colocar a inicialização (UI) na assinatura do método, o trabalho poderá ser atribuído na linha de código de chamada.

para que a moral da história seja segura com suas ações atrasadas, remova seus retornos de chamada ou cancele seus trabalhos e, é claro, confirme que você tem o ciclo de vida certo para tocar nos itens em seu retorno de retorno atrasado. As Coroutines também oferecem ações canceláveis.

Também é importante notar que você geralmente deve lidar com as várias exceções que podem surgir com as corotinas. Por exemplo, um cancelamento, uma exceção, um tempo limite, o que você decidir usar. Aqui está um exemplo mais avançado, se você decidir realmente começar a utilizar corotinas.

   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
Sem problemas, Rajiv, eu daria um passo adiante e mencionaria que, ao usar o Live Data, as corotinas podem ter consciência do ciclo de vida e cancelar-se automaticamente para evitar as chamadas de limpeza, mas não querem lançar muitas curvas de aprendizado em uma resposta;)
Sam

3

Eu criei um método mais simples para chamar isso.

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

Para usá-lo, basta ligar para: .CallWithDelay(5000, this, "DoSomething");


3
Reflexão para uma tarefa tão básica?
Max Ch

Desde a pergunta para chamar método semelhante ao iOS performSelector. esta é a melhor maneira de fazer.
HelmiB

3

Abaixo de um funciona quando você obtém,

java.lang.RuntimeException: Não é possível criar manipulador dentro do encadeamento que não chamou Looper.prepare ()

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

3

Usando o Kotlin, podemos conseguir fazendo o seguinte

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

2

É muito fácil usar o CountDownTimer. Para mais detalhes 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 você usa o RxAndroid, o tratamento de threads e erros fica muito mais fácil. O código a seguir é executado após um atraso

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

1

todo mundo parece esquecer de limpar o manipulador antes de postar uma nova mensagem ou executável. Caso contrário, eles poderiam se acumular e causar um mau comportamento.

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

Aqui está outra maneira complicada: não lançará exceção quando os elementos da interface do usuário executáveis ​​mudarem.

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

    }
}

Você pode chamar a animação assim:

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

A animação pode ser anexada a qualquer visualização.



1

Eu gosto de coisas mais limpas: aqui está minha implementação, código embutido para usar dentro do seu método

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

0

Uma solução adequada no 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

Solução semelhante, mas muito mais limpa de usar

Escreva esta função fora da classe

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

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

}

Uso:

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


apenas um nome, mantenha qualquer coisa lá. doé um método embutido por isso temos de usar `usá-lo como nome de variável
Manohar Reddy

Obrigado, mas por que esse nome de variável é usado? Quero dizer, qual é a função disso.
Skizo-ozᴉʞS

1
Pensei assim dodepois de um atraso de 3 segundos
Manohar Reddy

0

No Android, podemos escrever abaixo do código kotlin para atrasar a execução de qualquer função

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()
}
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.