如何在Android延迟后调用方法

我希望能够在指定的延迟后调用以下方法。在目标c中,有这样的东西:

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

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

public void DoSomething(){//do something here}
804716 次浏览

我建议使用定时器,它允许您安排在非常特定的时间间隔调用方法。这不会阻塞您的UI,并在执行方法时保持您的应用程序的共振。

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

看看这个demo:

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

备注:当问题没有指定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);⋮}

感谢您的所有精彩回答,我找到了最适合我需求的解决方案。

Handler myHandler = new DoSomething();Message m = new Message();m.obj = c;//passing a parameter heremyHandler.sendMessageDelayed(m, 1000);
class DoSomething extends Handler {@Overridepublic void handleMessage(Message msg) {MyObject o = (MyObject) msg.obj;//do something here}}

静态编程语言

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

Java

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

要导入的类是android.os.handler

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

在我的情况下,我无法使用任何其他答案。我使用了原生java定时器。

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

您可以在UIThread中使用Handler:

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

适用于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 {// SleepingThread.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}}

对于5秒后在UI Thread中执行的内容:

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

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

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

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

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

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

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

public static void CallWithDelay(long miliseconds, final Activity activity, final String methodName){new Handler().postDelayed(new Runnable() {
@Overridepublic 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");

这是另一个棘手的方法:当可运行的更改UI元素时,它不会抛出异常。

public class SimpleDelayAnimation extends Animation implements Animation.AnimationListener {
Runnable callBack;
public SimpleDelayAnimation(Runnable runnable, int delayTimeMilli) {setDuration(delayTimeMilli);callBack = runnable;setAnimationListener(this);}
@Overridepublic void onAnimationStart(Animation animation) {
}
@Overridepublic void onAnimationEnd(Animation animation) {callBack.run();}
@Overridepublic void onAnimationRepeat(Animation animation) {
}}

你可以这样调用动画:

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

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

这是我最短的解决方案:

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

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

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

使用CountDownTimer很容易。更多详情https://developer.android.com/reference/android/os/CountDownTimer.html

import android.os.CountDownTimer;
// calls onTick every second, finishes after 3 secondsnew CountDownTimer(3000, 1000) {
public void onTick(long millisUntilFinished) {Log.d("log", millisUntilFinished / 1000);}
public void onFinish() {// called after count down is finished}}.start();

如果您使用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);

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

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.

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

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

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

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

这就是答案在静态编程语言中,懒惰的人:

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

所以这里有几件事需要考虑,因为有很多方法可以揭穿这只猫的皮。尽管答案都已经被选中和选择了。我认为用适当的编码指南重新审视这一点很重要,以避免任何人仅仅因为“多数选择的简单答案”而走错方向。

所以首先让我们讨论简单的延迟后答案,这是这个线程中的获胜者选择的答案。

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

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

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

  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.VISIBLEmHandler.postDelayed({if(activity != null && activity?.isFinishing == false){txtNewInfo.visibility = View.GONE}}, NEW_INFO_SHOW_TIMEOUT_MS)}}

当然,处理onP暂停上的清理,这样它就不会碰到回调。

    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一样,你必须确保你处理取消,这样你就可以在延迟呼叫后进行活动检查,或者你可以像其他路由一样在onP暂停中取消它。

var mDelayedJob: Job? = nullfun 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 timeoutwithTimeout(4000) {//Moves to background threadwithContext(DefaultDispatcher) {mDeviceModelList.addArrayList(SSDBHelper.getAllDevices())}}
//Continues after async with context aboveshowFancyToast("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}")}}

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

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

下面一个工作当你得到,

java.lang.运行时异常:无法在线程内创建处理程序没有调用Looper.prepare()

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

KotlinJava多种方式

1.使用Handler

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

2.使用Timer任务

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() {@Overridepublic void run() {//Do something}}, 2000);

2.使用Timer

new Timer().schedule(new TimerTask() {@Overridepublic 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);

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

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

对于简单的行处理发布延迟,您可以执行以下操作:

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

我希望这能有所帮助

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

将此函数写出类

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

用法:

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

在Android中,我们可以编写下面的kotlin代码来延迟任何函数的执行

class MainActivity : AppCompatActivity() {
private lateinit var handler: Handler
override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)handler= Handler()handler.postDelayed({doSomething()},2000)}
private fun doSomething() {Toast.makeText(this,"Hi! I am Toast Message",Toast.LENGTH_SHORT).show()}}

有很多方法可以做到这一点,但最好的是使用像下面这样的处理程序

long millisecDelay=3000
Handler().postDelayed({// do your work here},millisecDelay)

使用静态编程语言,我们可以通过执行以下操作来实现

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

更安全-使用静态编程语言协程

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

在活动或片段中:

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

在ViewModel中:

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

在挂起功能:(静态编程语言协程)

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

如果你得到一个错误,生命周期范围未找到!-将此依赖项导入应用程序gradle文件:

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

如果您使用的是Java,则始终可以使用遗留Thread.sleep()

new Thread(() -> {
try {Thread.sleep(millis); //delay in milliseconds} catch (Exception e) {e.printStackTrace();}
yourMethod();
}).start();

这里唯一的问题是你不能做任何UI更新,因为它是一个单独的线程。

  • 静态编程语言
  • 从片段运行OnUiThread
  • 定时器

例子:

Timer().schedule(500) {activity?.runOnUiThread {// code}}

活动类上的另一个响应(java8)。

//hide with delaynew Thread(() -> {try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}this.runOnUiThread(() -> {myView.setVisibility(View.GONE);//this run on GUI});}).start();