如何在Android延迟后调用方法

ary*_*axt 716 java android delay handler

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

[self performSelector:@selector(DoSomething) withObject:nil afterDelay:5];
Run Code Online (Sandbox Code Playgroud)

android中有没有与java相同的方法?例如,我需要能够在5秒后调用方法.

public void DoSomething()
{
     //do something here
}
Run Code Online (Sandbox Code Playgroud)

kon*_*ity 1746

更好的版本 - Kotlin:

Handler().postDelayed({
  //Do something after 100ms
}, 100)
Run Code Online (Sandbox Code Playgroud)


更好的版本 - Java:

final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
  @Override
  public void run() {
    //Do something after 100ms
  }
}, 100);
Run Code Online (Sandbox Code Playgroud)


  • 此解决方案仅适用于UI线程.否则在普通线程上,你需要实现looper,这不是我认为最好的版本 (98认同)
  • @djechlin一个Handler必须始终链接到一个Looper,它实际上会处理你发布的Runnable().UI线程已经附带了一个Looper,因此你可以在UI线程上创建一个新的Handler()并将runnables直接发布到它上面.这些Runnables在UI线程上执行.要让Runnables在另一个线程上执行,你需要创建一个新线程,然后是Looper.prepare(),创建一个新的Handler()然后再生成Looper.loop().发布到这个新Handler的任何Runnable都将在这个新线程上执行.如果你不做这一切,post()将抛出异常. (36认同)
  • 如果需要,只要Runnable仍然在消息队列中,通过在`Handler`上调用`removeCallbacks(Runnable r)`,你也可以**取消**执行. (12认同)
  • 应该`导入​​android.os.handler` (9认同)
  • @olivier_sdg为什么需要实现looper? (2认同)
  • 谢谢!完美运行! (2认同)
  • 我这样使用它:`new Handler().postDelayed(() -> { //Do some after 100ms }, 100);` (2认同)

Jul*_*lle 310

在我的案例中,我无法使用任何其他答案.我使用了原生的Java Timer.

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

  • 这比使用Handler的更好,因为当Handler没有在UI线程上运行时它没有Looper问题. (41认同)
  • 您应该保留对计时器的引用,以便在不再需要时取消它,因为根据Android文档:"当不再需要计时器时,用户应调用cancel(),这将释放计时器的线程和其他资源.未明确取消的计时器可能会无限期地持有资源." (30认同)
  • 注意!这不在UI线程上运行.在ui线程上运行此命令会导致致命错误:android.view.ViewRootImpl $ CalledFromWrongThreadException:只有创建视图层次结构的原始线程才能触及其视图. (13认同)
  • @vovahost这只是因为你正在更新定时器块内的UI组件 (13认同)
  • 请注意,java.util.Timer(和TimerTask)将在JDK 9中弃用.TimerTask为任务创建新的线程,这不是很好. (7认同)
  • @VarvaraKalinina似乎在Java 9中不推荐使用Timer和TimerTask(https://docs.oracle.com/javase/9​​/docs/api/java/util/Timer.html)。 (2认同)

eri*_*son 182

注意:当问题没有将Android指定为上下文时,会给出此答案.有关Android UI线程的具体答案请查看此处.


看起来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);
  ?
}
Run Code Online (Sandbox Code Playgroud)

  • 作为旁注:这也允许您以后**取消**任务,这在某些情况下可能会有所帮助.只需存储对`worker.schedule()`返回的`ScheduledFuture <?>`的引用,并调用它的`cancel(boolean)`方法. (14认同)
  • @beetree它是`ScheduledExecutorService`的一个方法. (5认同)
  • 这从来没有为我打电话给Runnable (3认同)
  • 如果涉及ui线程对象,则这不起作用必须调用runOnUIThread(new runnable(){run()....}); 或者从run(){}内部使用handler对象发布runnable (3认同)

pom*_*ber 101

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

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

  • 确认,这是阻止调用looper.prepare并将整个内容放入UI线程的最佳解决方案. (7认同)

Hos*_*eeb 38

你可以在UIThread中使用Handler:

runOnUiThread(new Runnable() {

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

    }
});
Run Code Online (Sandbox Code Playgroud)


ary*_*axt 36

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

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
    }
}
Run Code Online (Sandbox Code Playgroud)


Osc*_*Ryz 20

看这个演示:

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?");
     }
}
Run Code Online (Sandbox Code Playgroud)


noo*_*oob 20

如果必须使用Handler,但是您进入另一个线程,则可以使用runonuithread在UI线程中运行处理程序.这将使您免于被要求呼叫的异常Looper.Prepare()

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

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

  • 这是有效的,我无法编辑你的帖子,因为愚蠢的SO规则至少要编辑6个字符,但是'new Handler'之后有'()'缺失它应该是'new Handler()' (4认同)
  • 您可以执行以下操作,而不是将所有内容放入UI线程中:new Handler(Looper.getMainLooper()) (2认同)

cod*_*zjx 16

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

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


Khe*_*raj 15

KotlinJava很多方法

1.使用 Handler

Handler().postDelayed({
    TODO("Do something")
    }, 2000)
Run Code Online (Sandbox Code Playgroud)

2.使用TimerTask

Timer().schedule(object : TimerTask() {
    override fun run() {
        TODO("Do something")
    }
}, 2000)
Run Code Online (Sandbox Code Playgroud)

甚至更短

Timer().schedule(timerTask {
    TODO("Do something")
}, 2000)
Run Code Online (Sandbox Code Playgroud)

或者最短的

Timer().schedule(2000) {
    TODO("Do something")
}
Run Code Online (Sandbox Code Playgroud)

3.使用 Executors

Executors.newSingleThreadScheduledExecutor().schedule({
    TODO("Do something")
}, 2, TimeUnit.SECONDS)
Run Code Online (Sandbox Code Playgroud)

在Java中

1.使用 Handler

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

2.使用 Timer

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

3.使用 ScheduledExecutorService

private static final ScheduledExecutorService worker = Executors.newSingleThreadScheduledExecutor();

Runnable runnable = new Runnable() {
  public void run() {
      // Do something
  }
  };
worker.schedule(runnable, 2, TimeUnit.SECONDS);
Run Code Online (Sandbox Code Playgroud)


Ale*_*ecs 14

这是我最简短的解决方案:

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


Vis*_*hnu 10

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); 
Run Code Online (Sandbox Code Playgroud)


Wil*_*ran 10

更安全 - 使用 Kotlin 协程

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

在活动或片段中:

lifecycleScope.launch { 
  delay(DELAY_MS)
  doSomething()
}
Run Code Online (Sandbox Code Playgroud)

在视图模型中:

viewModelScope.lanch {
  delay(DELAY_MS)
  doSomething()
}
Run Code Online (Sandbox Code Playgroud)

在挂起函数中:(Kotlin 协程)

suspend fun doSomethingAfter(){
    delay(DELAY_MS)
    doSomething()
}
Run Code Online (Sandbox Code Playgroud)

如果你得到一个错误与lifecycleScope没有发现-进口gradle这个文件:

implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0"
Run Code Online (Sandbox Code Playgroud)

  • 我认为协程方法更好。特别是当您的范围与组件 Activity、Fragment、自定义组件和生命周期绑定时。大多数时候,要求是在主机处于活动状态时执行方法。我还建议让 Job 实例支持逻辑取消。例如: val job =scope.launch {....} .... .... // 在延迟作业开始执行之前取消 job.cancel() (3认同)

vov*_*ost 9

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

new Handler().postDelayed(() -> callMyMethod(), 2000);
Run Code Online (Sandbox Code Playgroud)

如果您需要取消延迟的runnable,请使用:

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

// When you need to cancel all your posted runnables just use:
handler.removeCallbacksAndMessages(null);
Run Code Online (Sandbox Code Playgroud)


Nat*_*ate 6

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

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

  • Thread.sleep()优于Object.wait().等待意味着您希望得到通知并正在围绕某些活动进行同步.睡眠表示您只是希望在指定时间内不执行任何操作.如果您希望操作在稍后的某个时间异步发生,则可以使用计时器. (2认同)

Sam*_*Sam 6

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

因此,首先让我们讨论简单的延迟后答案,即本主题中的整体获胜者选择答案。

有几件事需要考虑。在后期延迟之后,您可能会遇到内存泄漏、死对象、已经消失的生命周期等等。因此,正确处理它也很重要。您可以通过多种方式执行此操作。

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

这是在回调上使用 UI 线程的一个简单示例,并在您点击回调时确认您的活动仍然有效。

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

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

    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)
        }
    }
Run Code Online (Sandbox Code Playgroud)

并且当然处理 onPause 上的清理,因此它不会命中回调。

    override fun onPause() {
        super.onPause()
        mHandler.removeCallbacks(null)
    }
Run Code Online (Sandbox Code Playgroud)

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

   fun doActionAfterDelay() 
        launch(UI) {
            delay(MS_TO_DELAY)           
            actionToTake()
        }
    }
Run Code Online (Sandbox Code Playgroud)

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

  fun doActionAfterDelay() = launch(UI){ 
      delay(MS_TO_DELAY)           
      actionToTake()
  }
Run Code Online (Sandbox Code Playgroud)

当然,就像 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}")
            }
        }
   }
}
Run Code Online (Sandbox Code Playgroud)

//处理清理

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
   }
}
Run Code Online (Sandbox Code Playgroud)

如果您将启动(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}")
            }
        }
Run Code Online (Sandbox Code Playgroud)


MrG*_*MrG 6

对于 Simple line Handle Post 延迟,您可以执行以下操作:

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

我希望这有帮助


小智 5

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

new Handler().postDelayed(() -> {/*your code here*/}, time);
Run Code Online (Sandbox Code Playgroud)


Cri*_*oGo 5

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

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

另外,下面可以是另一个干净有用的解决方案:

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