【问题标题】:How to call a method after a delay in Android如何在Android延迟后调用方法
【发布时间】:2011-03-05 13:41:30
【问题描述】:

我希望能够在指定的延迟后调用以下方法。 在目标 c 中有类似的内容:

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

在 android 和 java 中是否有这种方法的等价物? 例如我需要能够在 5 秒后调用一个方法。

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

【问题讨论】:

    标签: java android handler delay


    【解决方案1】:

    科特林

    Handler(Looper.getMainLooper()).postDelayed({
        //Do something after 100ms
    }, 100)
    

    Java

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

    【讨论】:

    • 此解决方案仅在 UI 线程上有用。否则在普通线程上,你需要实现循环器,这不是我认为的最佳版本
    • @olivier_sdg 为什么要实现looper?
    • @djechlin 处理程序必须始终链接到 Looper,它将实际处理您发布的 Runnable()。 UI 线程已经带有 Looper,因此您可以在 UI 线程上创建一个新的 Handler() 并将 post() Runnables 直接发送到它。这些 Runnable 在 UI 线程上执行。要让 Runnables 在另一个线程上执行,您需要创建一个新线程,然后是 Looper.prepare(),创建一个新的 Handler(),然后是 Looper.loop()。发布到这个新 Handler 的任何 Runnables 都将在这个新线程上执行。如果你不做这一切,post() 会抛出异常。
    • 如果您需要,您也可以通过在Handler 上调用removeCallbacks(Runnable r)取消只要Runnable 仍在消息队列中的执行。
    • 应该import android.os.handler
    【解决方案2】:

    在我的情况下,我无法使用任何其他答案。 我改用原生 java Timer。

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

    【讨论】:

    • 这比使用 Handler 的要好,因为当 Handler 不在 UI 线程上运行时,它没有 Looper 问题。
    • 您应该保留对计时器的引用,以便在不再需要它时取消它,因为根据 Android 文档:“当不再需要计时器时,用户应该调用 cancel(),它释放计时器的线程和其他资源。未明确取消的计时器可能会无限期地持有资源。"
    • 注意!这不在 UI 线程上运行。在 ui 线程上运行它会导致致命错误:android.view.ViewRootImpl$CalledFromWrongThreadException:只有创建视图层次结构的原始线程才能触摸其视图。
    • @vovahost 这只是因为您正在更新计时器块内的 UI 组件
    • 请注意,java.util.Timer(和 TimerTask)将在 JDK 9 中被弃用。TimerTask 为不太好的任务创建新线程。
    【解决方案3】:

    注意:这个答案是在问题没有将 Android 指定为上下文时给出的。对于特定于 Android UI 线程的答案look here.


    看起来 Mac OS API 让当前线程继续运行,并安排任务异步运行。在 Java 中,java.util.concurrent 包提供了等效功能。我不确定 Android 可能会施加什么限制。

    private static final ScheduledExecutorService worker = 
      Executors.newSingleThreadScheduledExecutor();
    
    void someMethod() {
      ⋮
      Runnable task = new Runnable() {
        public void run() {
          /* Do something… */
        }
      };
      worker.schedule(task, 5, TimeUnit.SECONDS);
      ⋮
    }
    

    【讨论】:

    • 这永远不会为我调用 Runnable
    • 附带说明:这还允许您稍后取消任务,这在某些情况下可能会有所帮助。只需存储对worker.schedule() 返回的ScheduledFuture<?> 的引用并调用其cancel(boolean) 方法。
    • 我认为这个答案已经过时了。 .schedule 似乎不再是 Runnable 的方法......? ://
    • @beetree 这是ScheduledExecutorService上的一个方法。
    • 如果涉及 ui 线程对象,这不起作用,您必须调用 runOnUIThread(new runnable(){ run()....});或使用 run(){ } 内部的处理程序对象发布可运行文件
    【解决方案4】:

    用于在 5 秒后在 UI 线程中执行某些操作:

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

    【讨论】:

    • 确认,这是防止调用 looper.prepare 并将整个事情放到 UI 线程中的最佳解决方案。
    • 谢谢你,帮我解决了 Looper 问题 :)
    • 我会小心在主循环器上创建一个处理程序,然后在这个线程中不应该完成长时间的任务
    【解决方案5】:

    Kotlin & Java 多种方式

    1。使用Handler

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

    2。使用 TimerTask

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

    甚至更短

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

    或者最短的是

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

    3。使用Executors

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

    在 Java 中

    1。使用Handler

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

    2。使用Timer

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

    3。使用ScheduledExecutorService

    private static final ScheduledExecutorService worker = Executors.newSingleThreadScheduledExecutor();
    
    Runnable runnable = new Runnable() {
      public void run() {
          // Do something
      }
      };
    worker.schedule(runnable, 2, TimeUnit.SECONDS);
    

    【讨论】:

    • @JanRabe 感谢您的建议。我很欣赏它。然而问题是How to call a method after a delay in Android。所以我专注于此。说到点子上了。否则,Java 泄漏对于开发人员来说是一个需要单独理解的大话题。
    • Handler().postDelayed({ }, 2000) 显示为贬损
    【解决方案6】:

    你可以在UIThread中使用Handler:

    runOnUiThread(new Runnable() {
                    
        @Override
        public void run() {
            new Handler().postDelayed(new Runnable() {
               @Override
               public void run() {
                   //add your code here
              }
            }, 1000);           
        }
    });
    

    【讨论】:

      【解决方案7】:

      感谢所有出色的答案,我找到了最适合我需求的解决方案。

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

      【讨论】:

      【解决方案8】:

      看这个演示:

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

      【讨论】:

        【解决方案9】:

        如果你必须使用Handler,但是你进入了另一个线程,你可以使用runonuithread在UI线程中运行handler。这将使您免于抛出异常要求调用Looper.Prepare()

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

        看起来很乱,但这是其中一种方式。

        【讨论】:

        • 这行得通,我无法编辑您的帖子,因为愚蠢的 SO 规则至少要编辑 6 个字符,但是在 'new Handler' 之后缺少 '()' 它应该是 'new Handler( )'
        • 您可以这样做:new Handler(Looper.getMainLooper())
        【解决方案10】:

        我更喜欢使用View.postDelayed()方法,下面是简单的代码:

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

        【讨论】:

        • 它不会冻结 ui 元素本身,因为它将被安排在视图处理程序上吗?
        • 不,发布的任务将在 1 秒内执行,但在此第二个 UI 线程会做其他有用的工作
        【解决方案11】:

        这是我最短的解决方案:

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

        【讨论】:

          【解决方案12】:

          如果您使用的是 Android Studio 3.0 及更高版本,则可以使用 lambda 表达式。 2秒后调用callMyMethod()方法:

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

          如果您需要取消延迟的可运行,请使用:

          Handler handler = new Handler();
          handler.postDelayed(() -> callMyMethod(), 2000);
          
          // When you need to cancel all your posted runnables just use:
          handler.removeCallbacksAndMessages(null);
          

          【讨论】:

          • 我们如何取消这个?
          • 我很惊讶这里有多少人会很高兴地转向 Kotlin,但却完全忽略了标准 Java 的 Lambda 表达式。
          【解决方案13】:

          更安全 - 使用 Kotlin 协程

          大多数答案都使用 Handler,但我给出了一个不同的解决方案来延迟活动、片段、视图模型与 Android Lifecycle ext。这种方式会在生命周期开始销毁时自动取消 - 避免内存泄漏或应用崩溃

          在 Activity 或 Fragment 中:

          lifecycleScope.launch { 
            delay(DELAY_MS)
            doSomething()
          }
          

          在 ViewModel 中:

          viewModelScope.lanch {
            delay(DELAY_MS)
            doSomething()
          }
          

          在挂起函数中:(Kotlin Coroutine)

          suspend fun doSomethingAfter(){
              delay(DELAY_MS)
              doSomething()
          }
          

          如果您在未找到 lifecycleScope 时遇到错误! - 将此依赖项导入应用程序 gradle 文件:

          implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.4.0"
          

          【讨论】:

          • 我觉得协程方式比较好。尤其是当您的范围与组件 Activity、Fragment、具有生命周期的自定义组件绑定时。大多数时候,要求是在主机还活着的时候执行一个方法。我还建议让 Job 实例支持逻辑取消。例如: val job = scope.launch {....} .... .... // 在延迟作业开始执行之前取消它 job.cancel()
          【解决方案14】:
          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); 
          

          【讨论】:

            【解决方案15】:

            我建议使用Timer,它允许您安排在非常特定的时间间隔内调用的方法。这不会阻塞您的 UI,并在执行该方法时让您的应用保持响应。

            另一个选项是wait(); 方法,这将阻塞当前线程指定的时间长度。如果您在 UI 线程上执行此操作,这将导致您的 UI 停止响应。

            【讨论】:

            • Thread.sleep() 优于 Object.wait()。等待意味着您希望收到通知并围绕某些活动进行同步。睡眠表示您只想在指定的时间内什么都不做。如果您希望操作在稍后的某个时间点异步发生,那么计时器是您的最佳选择。
            • 确实如此。这就是为什么我将它列为另一个选项;-)
            【解决方案16】:

            所以这里有一些事情需要考虑,因为有很多方法可以给这只猫剥皮。虽然答案都已经给出了选择和选择。我认为通过适当的编码指南重新审视这一点很重要,以避免任何人仅仅因为“多数选择简单答案”而走错方向。

            首先让我们讨论一下简单的延迟发布答案,它是本主题中的获胜者选择的答案。

            有几点需要考虑。在 post 延迟之后,您可能会遇到内存泄漏、死对象、已经消失的生命周期等等。所以正确处理也很重要。您可以通过多种方式做到这一点。

            为了现代发展,我会在KOTLIN中提供

            这是一个在回调中使用 UI 线程的简单示例,并在您点击回调时确认您的 Activity 仍然存在且运行良好。

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

            但是,这仍然不是完美的,因为如果活动已经消失,没有理由点击您的回调。所以更好的方法是保留对它的引用并像这样删除它的回调。

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

            当然还要处理 onPause 上的清理,这样它就不会触发回调。

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

            现在我们已经讨论了显而易见的问题,让我们来谈谈现代协程和 kotlin 的更简洁的选择:)。如果你还没有使用这些,那你真的错过了。

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

            或者,如果您想始终在该方法上启动 UI,您可以简单地执行以下操作:

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

            当然,就像 PostDelayed 一样,您必须确保处理取消,以便您可以在延迟调用之后进行活动检查,或者您可以像其他路线一样在 onPause 中取消它。

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

            //处理清理

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

            如果您将启动(UI)放入方法签名中,则可以在调用代码行中分配作业。

            故事的寓意是确保您的延迟操作安全,确保您删除回调,或取消您的工作,当然还要确认您有正确的生命周期来触摸延迟回调完成的项目。协程还提供可取消的操作。

            另外值得注意的是,您通常应该处理协程可能带来的各种异常。例如,取消、异常、超时,无论您决定使用什么。如果您决定真正开始使用协程,这里有一个更高级的示例。

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

            【讨论】:

            • 没问题 Rajiv,我会更进一步,并提到使用 Live Data 协程可以感知生命周期并自动取消以避免清理调用,但不想抛出太多学习曲线到一个答案;)
            【解决方案17】:

            对于一个简单的线句柄发布延迟,您可以执行以下操作:

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

            希望对你有帮助

            【讨论】:

              【解决方案18】:

              您可以将其用于最简单的解决方案:

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

              否则,下面可能是另一个干净有用的解决方案:

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

              【讨论】:

                【解决方案19】:

                您可以使用新引入的 lambda 表达式使其更简洁:

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

                【讨论】:

                  【解决方案20】:

                  使用 Kotlin,我们可以通过以下方式实现

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

                  【讨论】:

                    【解决方案21】:

                    如果您使用 RxAndroid,那么线程和错误处理会变得更加容易。以下代码在延迟后执行

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

                    【讨论】:

                      【解决方案22】:

                      我创建了更简单的方法来调用它。

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

                      要使用它,只需致电:.CallWithDelay(5000, this, "DoSomething");

                      【讨论】:

                      • 对这样一个基本任务的反思?
                      • 自问调用方法类似iOSperformSelector。这是最好的方法。
                      【解决方案23】:

                      当你得到以下一个作品时,

                      java.lang.RuntimeException:无法在线程内创建处理程序 没有调用 Looper.prepare()

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

                      【讨论】:

                        【解决方案24】:

                        使用CountDownTimer 非常简单。 更多详情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();
                        

                        【讨论】:

                          【解决方案25】:

                          我喜欢干净的东西: 这是我的实现,在您的方法中使用的内联代码

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

                          【讨论】:

                            【解决方案26】:

                            似乎每个人都忘记在 Handler 上发布新的可运行文件或消息之前对其进行清理。否则它们可能会累积并导致不良行为。

                            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.
                            

                            【讨论】:

                              【解决方案27】:

                              这里还有一个技巧:当runnable改变UI元素时它不会抛出异常。

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

                              你可以这样调用动画:

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

                              动画可以附加到任何视图。

                              【讨论】:

                                【解决方案28】:

                                Here is the answer 在 Kotlin 中你们这些懒惰、懒惰的人:

                                Handler().postDelayed({
                                //doSomethingHere()
                                }, 1000)
                                

                                【讨论】:

                                  【解决方案29】:

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

                                  【讨论】:

                                    【解决方案30】:

                                    类似的解决方案,但使用起来更清洁

                                    将此函数写在类之外

                                    fun delay(duration: Long, `do`: () -> Unit) {
                                    
                                        Handler().postDelayed(`do`, duration)
                                    
                                    }
                                    

                                    用法:

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

                                    【讨论】:

                                    • `做什么`?
                                    • 只是一个名字,什么都放在那里。 do 是一个内置方法,所以我们必须使用 ` 来将其用作变量名
                                    • 谢谢,为什么要用这个变量名呢?我的意思是它的功能是什么。
                                    • 延迟 3 秒后我想 do 这个
                                    猜你喜欢
                                    • 1970-01-01
                                    • 2014-05-06
                                    • 1970-01-01
                                    • 2012-06-25
                                    • 1970-01-01
                                    • 1970-01-01
                                    • 1970-01-01
                                    • 2015-12-20
                                    相关资源
                                    最近更新 更多