如何在Android中暂停/休眠线程或进程?


294

我想在两行代码之间暂停一下,让我解释一下:

->用户单击一个按钮(实际上是卡片),然后通过更改此按钮的背景来显示它:

thisbutton.setBackgroundResource(R.drawable.icon);

->假设1秒钟之后,我需要通过更改其背景回到按钮的先前状态:

thisbutton.setBackgroundResource(R.drawable.defaultcard);

->我尝试使用以下命令在这两行代码之间暂停线程:

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

但是,这不起作用。也许是过程而不是我需要暂停的线程?

我也尝试过(但是不起作用):

new Reminder(5);

有了这个:

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

如何暂停/休眠线程或进程?


4
哦,只需使用经典的线程暂停块:while(true){}
KristoferA

6
@ KristoferA-Huagati.com我不确定您是否在讽刺,或者确实有一些Dalvik / Android魔术,因此在Android上可以接受。你能澄清一下吗?很抱歉怀疑,但我问,因为(!conditionCheck()) {}通常不鼓励这样做。
悲惨的变量

“但是,这不起作用。” “我也尝试过(但不起作用)”。这是一个典型的例子,说明存在问题而不给出症状。这些尝试以什么方式无法满足您的要求?线程没有暂停吗?您收到错误消息了吗?
LarsH

Answers:


454

解决此问题的一种方法是使用Handler.postDelayed()方法。一些Google 培训材料提出了相同的解决方案。

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

但是,有些人指出,上述解决方案会导致内存泄漏,因为它使用了一个非静态的内部和匿名类,该类隐式持有对其外部类(活动)的引用。当活动上下文被垃圾回收时,这是一个问题。

避免内存泄漏的更复杂的解决方案将子类化为,HandlerRunnable在活动内部使用静态内部类,因为静态内部类不持有对其外部类的隐式引用:

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

请注意,会Runnable使用对Activity 的WeakReference,这在需要访问UI的静态类中是必需的。


3
它可以工作,但这是在整个代码中引入延迟的一种非常不便的方法,对吗?
Ehtesh Choudhury 2012年

4
我不确定您所说的“不便”是什么意思。处理程序的postDelayed方法旨在告诉Android,您希望在经过一定时间后执行一些代码。
tronman

27
2年后,此代码对我有所帮助!谢谢@tronman!:)
梅文礼(Melvin Lai)

2
您可以像这样简单地将其复制到另一个(最终)变量,final Button mynewbutton = mybutton;然后mynewbutton在Handler和Runnable中使用。
Dzhuneyt

2
@MelvinLai 5年后,这段代码对我有所帮助!:)
gabrieloliveira 2015年

191

您可以尝试这个很短的

SystemClock.sleep(7000);

警告:永远不要在UI线程上执行此操作。

用这个睡觉。后台线程。


完整的解决方案是: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);
            }
        });

不创建tmp处理程序。同样,此解决方案比@tronman更好,因为我们不保留Handler的视图。同样,在坏线程处创建的处理程序也没有问题;)

文献资料

公共静态无效睡眠(长毫秒)

在API级别1中添加

在返回之前等待给定的毫秒数(uptimeMillis)。与sleep(long)相似,但不抛出InterruptedException ; 将interrupt()事件推迟到下一个可中断的操作。难道不返回,直到至少指定的毫秒数已过。

参量

ms,返回之前要进入睡眠状态,以毫秒为单位。

View类的postDelayed代码:

/**
 * <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
并让Android UI冻结?不要这样
shkschneider

3
Ctrl + Shift + O(Eclipse自动导入)
Dawid Drozd 2014年

13
Gelldur,您错过了@RichieHH的评论的意思。与您发布3年之前接受答案相比,您的建议无助于OP解决他的问题。原因:如OP所示和接受的答案中所示,该代码都在UI处理程序中运行OMG of course do this in background thread除非您显示如何将其放在后台线程中,否则说的话是无关紧要的。到那时,您会发现答案比已经接受的答案复杂得多。我是否提到三年前接受了更好的解决方案?:P
ToolmakerSteve14年

2
...忘了提起这个建议,使该建议对这个问题格格不入的原因是,OP明确希望在延迟后执行UI操作。因此,您将拥有一个解决方案,其中您已经创建了一个后台线程(已经比解决该问题所需的权重更大),入睡,然后必须(以某种方式)回到UI线程。Handler + postRunnable一步就能完成所有这些工作。没有创建第二个线程的系统开销。
ToolmakerSteve

2
@ToolmakerSteve现在很高兴:D吗?主要问题是:“如何在Android中暂停/休眠线程或进程?” 所以我的回答很简单:)。如果有人用谷歌搜索“如何休眠线程”,则会显示此问题。他/她可能正在寻找我的答案:P。但是好吧,我添加了完整的答复;)
戴维德罗兹(Dawid Drozd

28

我用这个:

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

4
什么是e.getLocalizedMessage()应该做的?
菲利普·里查特

当我需要简单,通用和快速的异常消息时,我使用e.getLocalizedMessage()
Byt3 2012年

1
但是我敢打赌,如果OP要求在UI click方法内部进行操作,它将不会对UI进行进一步的更新,否则您不会这样做。公认的Handler/postDelayed解决方案具有两个优点:(1)避免了第二线程的系统开销,(1)在UI线程上运行,因此可以进行UI更改而不会引起异常。
ToolmakerSteve

1
与问题的主要目标没有直接关系,但您不应该抓住异常。而是捕获InterruptedException。通过捕获Exception,您将捕获可能出错的所有内容,从而隐藏了原本可以捕获的问题。
2016年

16

您可能不想这样做。通过sleep()在按钮单击的事件处理程序中放置一个明确的控件,您实际上会锁定整个UI一秒钟。一种选择是使用某种单次Timer。创建一个TimerTask,将背景色改回默认颜色,并在Timer上安排它。

另一种可能性是使用Handler。有一个关于某人的教程,该人从使用计时器切换为使用处理程序。

顺便说一句,您不能暂停一个过程。一个Java(或Android)进程至少具有1个线程,并且您只能休眠线程。


14

我使用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(); 

这很有用。
UzaySan,

9

这是我一天结束时所做的-现在可以正常使用:

@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
为什么不发布延迟?无需计时器。
RichieHH 2014年

8

除了Yankowsky先生的答案,您还可以使用postDelayed()。这在任何View(例如您的卡)上都可用,并且需要一个Runnable和延迟时间。它执行该Runnable延迟之后。


5

这是我的例子

创建一个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
在问题已经解决好多年后又增加了一个答案,与这个问题无关,因为它没有满足人们对能够执行UI工作的要求。您无法在此处执行UI工作,因为您正在新线程上运行。不在UI线程上。
ToolmakerSteve

1
在UX方面,向用户显示阻止对话框以加载数据不是很好。
2Dee 15'Mar 16'at

4

或者您可以使用:

android.os.SystemClock.sleep(checkEvery)

优点是不需要包裹try ... catch


0

如果您使用Kotlin和协程,则只需

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

如果您需要更新UI

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

0

我知道这是一个旧线程,但是在Android文档中我找到了一个非常适合我的解决方案...

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

希望这可以帮助某人...


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

再次确保您可以将其与tronman答案中所述的“静态类”方法结合使用

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.