Como pausar / suspender o thread ou o processo no Android?


294

Quero fazer uma pausa entre duas linhas de código, deixe-me explicar um pouco:

-> o usuário clica em um botão (na verdade, um cartão) e eu o mostro alterando o plano de fundo desse botão:

thisbutton.setBackgroundResource(R.drawable.icon);

-> depois, digamos 1 segundo, preciso voltar ao estado anterior do botão, alterando novamente o plano de fundo:

thisbutton.setBackgroundResource(R.drawable.defaultcard);

-> Tentei pausar o thread entre essas duas linhas de código com:

try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

Entretanto, isso não funciona. Talvez seja o processo e não o segmento que eu preciso pausar?

Eu também tentei (mas não funciona):

new Reminder(5);

Com isso:

public class Reminder {

Timer timer;

        public Reminder(int seconds) {
            timer = new Timer();
            timer.schedule(new RemindTask(), seconds*1000);
        }

        class RemindTask extends TimerTask {
            public void run() {
                System.out.format("Time's up!%n");
                timer.cancel(); //Terminate the timer thread
            }
        }  
    }

Como posso pausar / adormecer o thread ou processo?


4
Ah, basta usar o bloco de pausa de thread clássico: while (true) {}
— KristoferA

6
@ KristoferA-Huagati.com Não tenho certeza se você está sendo sarcástico ou se há alguma mágica do Dalvik / Android para que isso seja aceitável no Android. Você pode esclarecer por favor? Desculpe por duvidar, mas pergunto, porque enquanto (!conditionCheck()) {}geralmente é desencorajado.
— Variável miserável

"Entretanto, isso não funciona." "Eu também tentei (mas não funciona)" Este é um exemplo clássico de dizer que há um problema sem apresentar os sintomas. De que maneira essas tentativas falharam em atender aos seus requisitos? O segmento não parou? você recebeu uma mensagem de erro?
— Larsh

Respostas:


454

Uma solução para esse problema é usar o método Handler.postDelayed () . Alguns materiais de treinamento do Google sugerem a mesma solução.

@Override
public void onClick(View v) {
    my_button.setBackgroundResource(R.drawable.icon);

    Handler handler = new Handler(); 
    handler.postDelayed(new Runnable() {
         @Override 
         public void run() { 
              my_button.setBackgroundResource(R.drawable.defaultcard); 
         } 
    }, 2000); 
}

No entanto, alguns apontaram que a solução acima causa um vazamento de memória porque usa uma classe interna e anônima não estática que contém implicitamente uma referência à sua classe externa, a atividade. Este é um problema quando o contexto da atividade é coletado como lixo.

Uma solução mais complexa que evita o vazamento de memória subclassifica as classes internas Handlere Runnableestáticas dentro da atividade, pois as classes internas estáticas não possuem uma referência implícita à sua classe externa:

private static class MyHandler extends Handler {}
private final MyHandler mHandler = new MyHandler();

public static class MyRunnable implements Runnable {
    private final WeakReference<Activity> mActivity;

    public MyRunnable(Activity activity) {
        mActivity = new WeakReference<>(activity);
    }

    @Override
    public void run() {
        Activity activity = mActivity.get();
        if (activity != null) {
            Button btn = (Button) activity.findViewById(R.id.button);
            btn.setBackgroundResource(R.drawable.defaultcard);
        }
    }
}

private MyRunnable mRunnable = new MyRunnable(this);

public void onClick(View view) {
    my_button.setBackgroundResource(R.drawable.icon);

    // Execute the Runnable in 2 seconds
    mHandler.postDelayed(mRunnable, 2000);
}

Observe que ele Runnableusa um WeakReference para a atividade, necessário em uma classe estática que precisa de acesso à interface do usuário.


3
Funciona, mas é uma maneira bastante inconveniente de introduzir atrasos no código, certo?
— Ehtesh Choudhury

4
Não sei o que você quer dizer com "inconveniente". O método postDelayed do Handler foi desenvolvido para informar ao Android que você deseja que um pouco de código seja executado após um certo período de tempo.
— tronman

27
depois de 2 anos e esse código me ajudou! obrigado @tronman !! :)
— Melvin Lai

2
Você pode simplesmente copiá-lo para outra variável (final) assim final Button mynewbutton = mybutton;e usar mynewbuttonno manipulador e no executável a partir daí.
— Dzhuneyt

2
@MelvinLai depois de 5 anos e esse código me ajudou! :)
— gabrieloliveira 26/11

191

Você pode tentar este aqui é curto

SystemClock.sleep(7000);

AVISO : Nunca, nunca, faça isso em um thread da interface do usuário.

Use isso para dormir, por exemplo. segmento de plano de fundo.


A solução completa para o seu problema será: Esta está disponível API 1

findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(final View button) {
                button.setBackgroundResource(R.drawable.avatar_dead);
                final long changeTime = 1000L;
                button.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        button.setBackgroundResource(R.drawable.avatar_small);
                    }
                }, changeTime);
            }
        });

Sem criar o manipulador tmp. Além disso, esta solução é melhor que o @tronman, porque não mantemos a visão do Handler. Também não temos problemas com o Handler criado em threads ruins;)

Documentação

sono vazio estático público (ms longa)

Adicionado no nível 1 da API

Aguarda um determinado número de milissegundos (de uptimeMillis) antes de retornar. Semelhante ao sono (longo), mas não gera InterruptedException ; Os eventos interrupt () são adiados até a próxima operação interruptível. Não retorna até que pelo menos o número especificado de milissegundos tenha decorrido.

Parâmetros

ms para dormir antes de retornar, em milissegundos de tempo de atividade.

Código para postDelayed da classe View:

/**
 * <p>Causes the Runnable to be added to the message queue, to be run
 * after the specified amount of time elapses.
 * The runnable will be run on the user interface thread.</p>
 *
 * @param action The Runnable that will be executed.
 * @param delayMillis The delay (in milliseconds) until the Runnable
 *        will be executed.
 *
 * @return true if the Runnable was successfully placed in to the
 *         message queue.  Returns false on failure, usually because the
 *         looper processing the message queue is exiting.  Note that a
 *         result of true does not mean the Runnable will be processed --
 *         if the looper is quit before the delivery time of the message
 *         occurs then the message will be dropped.
 *
 * @see #post
 * @see #removeCallbacks
 */
public boolean postDelayed(Runnable action, long delayMillis) {
    final AttachInfo attachInfo = mAttachInfo;
    if (attachInfo != null) {
        return attachInfo.mHandler.postDelayed(action, delayMillis);
    }
    // Assume that post will succeed later
    ViewRootImpl.getRunQueue().postDelayed(action, delayMillis);
    return true;
}

22
E deixar a interface do usuário do Android congelar? Não faça isso.
— shkschneider

3
Ctrl + Shift + O (Eclipse importação automática)
— Dawid Drozd

13
Gelldur, você está perdendo o objetivo do comentário de @ RichieHH. Comparado com a resposta que foi aceita três anos antes de você postar , o que você sugere não ajuda o OP a resolver o problema dele. Motivo: o código, conforme mostrado pelo OP e conforme mostrado na resposta aceita, está sendo executado dentro de um manipulador da interface do usuário . Dizer OMG of course do this in background threadé irrelevante, a menos que você mostre COMO colocá-lo no thread de segundo plano. Nesse momento, você descobrirá que tem uma resposta mais complicada do que a já aceita. Mencionei que uma solução melhor foi aceita há três anos? : P
— ToolmakerSteve

2
... esqueci de mencionar que o que torna essa sugestão particularmente desagradável à questão é que o OP deseja explicitamente executar ações da interface do usuário após o atraso. Portanto, você teria uma solução na qual criou um encadeamento em segundo plano (já mais pesado do que o necessário para resolver o problema), dormiu e, em seguida, precisará (de alguma forma) voltar ao encadeamento da interface do usuário. Handler + postRunnablerealiza tudo isso, em uma única etapa. Sem a sobrecarga do sistema para criar um segundo encadeamento.
— Home

2
@ToolmakerSteve feliz agora: D? A principal questão é: "Como pausar / suspender o thread ou processo no Android?" então minha resposta foi simples :). Se alguém procurar no google por "como dormir o tópico", essa pergunta será exibida. Ele / Ela provavelmente está procurando minha resposta: P. Mas ok eu adicionei resposta completa;)
— Dawid Drozd

28

Eu uso isso:

Thread closeActivity = new Thread(new Runnable() {
  @Override
  public void run() {
    try {
      Thread.sleep(3000);
      // Do some stuff
    } catch (Exception e) {
      e.getLocalizedMessage();
    }
  }
});

4
O que você e.getLocalizedMessage()deve fazer?
— Philipp Reichart

eu uso e.getLocalizedMessage () quando eu preciso de um simples, mensagem de exceção geral e rápida
— Byt3

1
Mas aposto que você não faz isso na situação em que o OP pergunta, dentro de um método de clique na interface do usuário, que fará novas atualizações na interface do usuário. A Handler/postDelayedsolução aceita possui duas vantagens: (1) evita a sobrecarga do sistema do 2º encadeamento, (1) é executado no encadeamento da interface do usuário, portanto, pode fazer alterações na interface do usuário sem causar uma exceção.
— Home

1
Não está diretamente relacionado ao objetivo principal da pergunta, mas você não deve capturar Exception. Em vez disso, pegue InterruptedException. Ao capturar Exceção, você capturará tudo o que poderia dar errado, ocultando problemas que você capturaria de outra forma.
— precisa

16

Você provavelmente não quer fazer dessa maneira. Ao colocar um explícito sleep()no seu manipulador de eventos clicado no botão, você realmente trava a interface do usuário inteira por um segundo. Uma alternativa é usar algum tipo de timer de disparo único . Crie uma TimerTask para alterar a cor do plano de fundo para a cor padrão e agende-a no Timer.

Outra possibilidade é usar um manipulador . Há um tutorial sobre alguém que deixou de usar um timer para usar um manipulador.

Aliás, você não pode pausar um processo. Um processo Java (ou Android) possui pelo menos 1 thread e você pode dormir somente com threads.


14

Eu uso CountDownTime

new CountDownTimer(5000, 1000) {

    @Override
    public void onTick(long millisUntilFinished) {
        // do something after 1s
    }

    @Override
    public void onFinish() {
        // do something end times 5s
    }

}.start(); 

isso é útil.
— UzaySan

9

Foi o que fiz no final do dia - funciona bem agora:

@Override
    public void onClick(View v) {
        my_button.setBackgroundResource(R.drawable.icon);
        // SLEEP 2 SECONDS HERE ...
        final Handler handler = new Handler(); 
        Timer t = new Timer(); 
        t.schedule(new TimerTask() { 
                public void run() { 
                        handler.post(new Runnable() { 
                                public void run() { 
                                 my_button.setBackgroundResource(R.drawable.defaultcard); 
                                } 
                        }); 
                } 
        }, 2000); 
    }

2
Por que não postDelayed? Não é necessário temporizador.
— 21914 RichieHH

8

Além das respostas do Sr. Yankowsky, você também pode usar postDelayed(). Está disponível em qualquer View(por exemplo, seu cartão) e leva um Runnableperíodo de atraso. Ele executa o Runnabledepois desse atraso.


5

Este é o meu exemplo

Crie um Java Utils

    import android.app.ProgressDialog;
    import android.content.Context;
    import android.content.Intent;

    public class Utils {

        public static void showDummyWaitingDialog(final Context context, final Intent startingIntent) {
            // ...
            final ProgressDialog progressDialog = ProgressDialog.show(context, "Please wait...", "Loading data ...", true);

            new Thread() {
                public void run() {
                    try{
                        // Do some work here
                        sleep(5000);
                    } catch (Exception e) {
                    }
                    // start next intent
                    new Thread() {
                        public void run() {
                        // Dismiss the Dialog 
                        progressDialog.dismiss();
                        // start selected activity
                        if ( startingIntent != null) context.startActivity(startingIntent);
                        }
                    }.start();
                }
            }.start();  

        }

    }    

3
Ainda outra resposta foi adicionada anos depois que a pergunta já foi bem resolvida, que não é relevante para a pergunta, porque não lida com o desejo declarado de poder executar o trabalho da interface do usuário. Você não pode fazer o trabalho da interface do usuário aqui, porque você está executando um novo thread. Não está no thread da interface do usuário.
— Home

1
No lado UX, mostrar uma caixa de diálogo de bloqueio para o usuário carregar dados não é ... bom.
— 2Dee

4

Ou você pode usar:

android.os.SystemClock.sleep(checkEvery)

que tem a vantagem de não exigir uma embalagem try ... catch.


0

Se você usa Kotlin e corotinas , pode simplesmente fazer

GlobalScope.launch {
   delay(3000) // In ms
   //Code after sleep
}

E se você precisar atualizar a interface do usuário

GlobalScope.launch {
  delay(3000)
  GlobalScope.launch(Dispatchers.Main) {
    //Action on UI thread
  }
}

0

Sei que esse é um tópico antigo, mas na documentação do Android encontrei uma solução que funcionou muito bem para mim ...

new CountDownTimer(30000, 1000) {

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

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

https://developer.android.com/reference/android/os/CountDownTimer.html

Espero que isso ajude alguém ...


0
  class MyActivity{
    private final Handler handler = new Handler();
    private Runnable yourRunnable;
    protected void onCreate(@Nullable Bundle savedInstanceState) {
       // ....
       this.yourRunnable = new Runnable() {
               @Override
               public void run() {
                   //code
               }
            };

        this.handler.postDelayed(this.yourRunnable, 2000);
       }


     @Override
  protected void onDestroy() {
      // to avoid memory leaks
      this.handler.removeCallbacks(this.yourRunnable);
      }
    }

E para ter certeza de que você pode combiná-lo com o método "classe estática", conforme descrito na resposta do tronman

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.