【问题标题】:How to pause / sleep thread or process in Android?如何在 Android 中暂停/睡眠线程或进程?
【发布时间】:2010-12-04 00:02:12
【问题描述】:

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

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

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

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

【问题讨论】:

  • 哦,就用经典的线程暂停块:while (true) {}
  • @KristoferA-Huagati.com 我不确定你是在讽刺还是确实有一些 Dalvik/Android 的魔力,所以这在 Android 上是可以接受的。你能澄清一下吗?很抱歉怀疑,但我问是因为 (!conditionCheck()) {} 通常不鼓励。
  • “但是,这不起作用。” “我也尝试过(但它不起作用)”这是一个典型的例子,说明有问题而不给出症状。这些尝试在哪些方面未能满足您的要求?线程没有暂停吗?您收到错误消息了吗?

标签: android process multithreading


【解决方案1】:
  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 答案中描述的“静态类”方法结合使用

【讨论】:

    【解决方案2】:

    我知道这是一个旧线程,但在 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

    希望这对某人有所帮助...

    【讨论】:

      【解决方案3】:

      如果你使用 Kotlin 和 coroutines,你可以这样做

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

      如果你需要更新 UI

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

      【讨论】:

        【解决方案4】:

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

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

        然而,有人指出causes a memory leak 上面的解决方案是因为它使用了一个非静态的内部匿名类,该类隐含地持有对其外部类活动的引用。当活动上下文被垃圾收集时,这是一个问题。

        避免内存泄漏的更复杂的解决方案是在活动内部使用静态内部类将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 的静态类中是必需的。

        【讨论】:

        • 它可以工作,但这是在整个代码中引入延迟的一种非常不方便的方式,对吧?
        • 我不确定您所说的“不方便”是什么意思。 Handler 的 postDelayed 方法旨在告诉 Android 您希望在经过一定时间后执行一些代码。
        • 2 年后,这段代码对我有帮助!谢谢@tronman!! :)
        • 您可以简单地将其复制到另一个(最终)变量,例如 final Button mynewbutton = mybutton;,然后在 Handler 和 Runnable 中使用 mynewbutton
        • @MelvinLai 5 年后,这段代码帮助了我! :)
        【解决方案5】:

        或者你可以使用:

        android.os.SystemClock.sleep(checkEvery)
        

        它的优点是不需要包装try ... catch

        【讨论】:

          【解决方案6】:

          我使用倒计时

          new CountDownTimer(5000, 1000) {
          
              @Override
              public void onTick(long millisUntilFinished) {
                  // do something after 1s
              }
          
              @Override
              public void onFinish() {
                  // do something end times 5s
              }
          
          }.start(); 
          

          【讨论】:

          • 这很有用。
          【解决方案7】:

          你可以试试这个

          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 Handler。这个解决方案也比@tronman 更好,因为我们不保留 Handler 的视图。 此外,我们对在坏线程上创建的 Handler 没有问题;)

          Documentation

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

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

          【讨论】:

          • 然后让 Android UI 冻结?不要这样做。
          • Ctrl + Shift + O(Eclipse 自动导入)
          • Gelldur,你错过了@RichieHH 评论的重点。与在您发布前 3 年被接受的答案相比,您的建议无助于 OP 解决他的问题。原因:代码(如 OP 所示,以及已接受的答案所示)在 UI 处理程序中运行。说OMG of course do this in background thread 是无关紧要的,除非您展示如何将其放入后台线程。到那时,你会发现你的答案比已经接受的答案更复杂。我有没有提到三年前接受了更好的解决方案? :P
          • ... 忘了提到,是什么让这个建议特别偏离了问题的目的,是 OP 明确希望在延迟后执行 UI 操作。因此,您将有一个解决方案,其中您创建了一个后台线程(已经比解决问题所需的重量更大),睡觉,然后您必须(不知何故)回到 UI 线程。 Handler + postRunnable 一步完成所有这些。没有创建第二个线程的系统开销。
          • @ToolmakerSteve 现在很开心:D?主要问题是:“如何在 Android 中暂停/休眠线程或进程?”所以我的回答很简单:)。如果有人用谷歌搜索“如何睡觉线程”这个问题会显示。他/她可能正在寻找我的答案:P。但是好的,我添加了完整的回复;)
          【解决方案8】:

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

          【讨论】:

            【解决方案9】:

            我用这个:

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

            【讨论】:

            • e.getLocalizedMessage() 应该做什么?
            • 当我需要简单、通用和快速的异常消息时,我会使用 e.getLocalizedMessage()
            • 但我敢打赌,在 OP 询问的情况下,您不会在 UI click 方法中执行此操作,这将对 UI 进行进一步更新。公认的Handler/postDelayed 解决方案有两个优点:(1)避免了2nd线程的系统开销,(1)在UI线程上运行,因此可以在不引起异常的情况下进行UI更改。
            • 与问题的主要目标没有直接关系,但您不应捕获异常。而是捕获 InterruptedException。通过捕获异常,您将捕获所有可能出错的内容,从而隐藏您原本会捕获的问题。
            【解决方案10】:

            这是我的例子

            创建一个 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();  
            
                    }
            
                }    
            

            【讨论】:

            • 在问题已经得到很好解决多年后添加的另一个答案与问题无关,因为它不涉及能够执行 UI 工作的既定愿望。你不能在这里做 UI 工作,因为你在一个新线程上运行。不在 UI 线程上。
            • 在 UX 方面,向用户显示阻止对话框以加载数据......不好。
            • 如果我们不需要更改ui,我想这种方法比在主线程上运行Handlers更好。但是 Timer 类或在不同的线程上显式运行 Handler 呢?
            【解决方案11】:

            这就是我在一天结束时所做的 - 现在工作正常:

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

            【讨论】:

            • 为什么不延迟发布?不需要计时器。
            【解决方案12】:

            你可能不想那样做。通过在您的按钮单击事件处理程序中放置一个显式的sleep(),您实际上会锁定整个 UI 一秒钟。一种替代方法是使用某种单发Timer。创建TimerTask 将背景颜色更改回默认颜色,并在 Timer 上进行调度。

            另一种可能性是使用Handlera tutorial 有人从使用 Timer 切换到使用 Handler。

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2011-05-14
              • 2015-06-28
              • 1970-01-01
              • 2019-07-18
              • 1970-01-01
              • 2011-11-22
              相关资源
              最近更新 更多