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?

Hubert
fonte
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.

tronman
fonte
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;
}
Dawid Drozd
fonte
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();
    }
  }
});
Byt3
fonte
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.

Daniel Yankowsky
fonte
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(); 
vudandroid
fonte
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); 
    }
Hubert
fonte
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.

CommonsWare
fonte
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();  

        }

    }    
Stefano
fonte
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.

aaaa
fonte
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
  }
}
Guillaume
fonte
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 ...

John Byrne
fonte
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

principiante
fonte