Java 在Android中如何在延迟后调用方法

Java 在Android中如何在延迟后调用方法,java,android,handler,delay,Java,Android,Handler,Delay,我希望能够在指定的延迟后调用以下方法。 在目标c中,有如下内容: [self performSelector:@selector(DoSomething) withObject:nil afterDelay:5]; 在android和java中是否有类似的方法? 例如,我需要能够在5秒后调用一个方法 public void DoSomething() { //do something here } 我建议,它允许您计划在非常特定的时间间隔调用一个方法。这不会阻塞您的UI,并在执行方

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

[self performSelector:@selector(DoSomething) withObject:nil afterDelay:5];
在android和java中是否有类似的方法? 例如,我需要能够在5秒后调用一个方法

public void DoSomething()
{
     //do something here
}
我建议,它允许您计划在非常特定的时间间隔调用一个方法。这不会阻塞您的UI,并在执行方法时保持应用程序的响应

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

请参阅此演示:

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
包提供。我不确定安卓可能会施加哪些限制

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 here
myHandler.sendMessageDelayed(m, 1000);

class DoSomething extends Handler {
    @Override
    public void handleMessage(Message msg) {
      MyObject o = (MyObject) msg.obj;
      //do something here
    }
}
科特林
处理程序(Looper.getMainLooper()).postDelayed({
//100毫秒后做点什么
}, 100)

JAVA
final Handler=new Handler(Looper.getMainLooper());
handler.postDelayed(新的Runnable(){
@凌驾
公开募捐{
//100毫秒后做点什么
}
}, 100);


在我的案例中,我无法使用任何其他答案。 我使用了本机java计时器

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

可以在UIThread中使用处理程序:

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

    }
});

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

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

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

如果必须使用该处理程序,但您进入了另一个线程,则可以使用
runonuithread
在UI线程中运行该处理程序。这将使您免于请求调用
Looper.Prepare()


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

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

mView.postDelayed(new Runnable() {
    @Override
    public 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() {

            @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,这是“DoSomething”)

这里是另一个棘手的方法:当可运行的更改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));

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

以下是我的最短解决方案:

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

通过使用新引入的lambda表达式,可以使其更加清晰:

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

使用
倒计时功能非常简单。
欲知详情


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

new Handler().postDelayed(() -> callMyMethod(), 2000);
如果需要取消延迟的runnable,请使用以下命令:

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

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

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

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() {
    @Override
    public 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)

所以这里有一些事情要考虑,因为有很多方法来保护这只猫。虽然答案都已经被选择了。我认为这一点很重要,要用正确的编码准则来重新审视,以避免任何人仅仅因为“多数选择简单答案”而走错方向

因此,首先让我们讨论一下简单的延迟后答案,它是本线程中的获胜者选择的答案

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

为了现代发展,我将在科特林供应

下面是一个在回调上使用UI线程的简单示例,当您点击回调时,确认您的活动仍然处于活动状态

  Handler(Looper.getMainLooper()).postDelayed({
            if(activity != null && activity?.isFinishing == false){
                txtNewInfo.visibility = View.GONE
            }
        }, NEW_INFO_SHOW_TIMEOUT_MS)
    override fun onPause() {
        super.onPause()
        mHandler.removeCallbacks(null)
    }
但是,这仍然不是完美的,因为如果活动已消失,则没有理由点击回调。因此,更好的方法是保留对它的引用,并像这样删除它的回调

    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上的清理,这样它就不会命中回调

  Handler(Looper.getMainLooper()).postDelayed({
            if(activity != null && activity?.isFinishing == false){
                txtNewInfo.visibility = View.GONE
            }
        }, NEW_INFO_SHOW_TIMEOUT_MS)
    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}")
            }
        }
我喜欢干净的东西: 这是我的实现,在方法中使用的内联代码

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

下面的一个工作时,你得到

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

Kotlin
&
Java
多种方式 1.使用
处理程序
2.使用TimerTask 甚至更短

Timer().schedule(timerTask {
    TODO("Do something")
}, 2000)
或者是最短的

Timer().schedule(2000) {
    TODO("Do something")
}
3.使用
执行器Handler().postDelayed({
    TODO("Do something")
    }, 2000)
Timer().schedule(object : TimerTask() {
    override fun run() {
        TODO("Do something")
    }
}, 2000)
Timer().schedule(timerTask {
    TODO("Do something")
}, 2000)
Timer().schedule(2000) {
    TODO("Do something")
}
Executors.newSingleThreadScheduledExecutor().schedule({
    TODO("Do something")
}, 2, TimeUnit.SECONDS)
new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        //Do something
    }
}, 2000);
new Timer().schedule(new TimerTask() {          
    @Override
    public void run() {
        // Do something
    }
}, 2000);
private static final ScheduledExecutorService worker = Executors.newSingleThreadScheduledExecutor();

Runnable runnable = new Runnable() {
  public void run() {
      // Do something
  }
  };
worker.schedule(runnable, 2, TimeUnit.SECONDS);
   Observable.timer(delay, TimeUnit.SECONDS)
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(aLong -> {
           // Execute code here
        }, Throwable::printStackTrace);
new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        // Do someting
    }
}, 3000);
fun delay(duration: Long, `do`: () -> Unit) {

    Handler().postDelayed(`do`, duration)

}
delay(5000) {
    //Do your work here
}
Handler().postDelayed({
    // do something after 1000ms 
}, 1000)
lifecycleScope.launch { 
  delay(DELAY_MS)
  doSomething()
}
viewModelScope.lanch {
  delay(DELAY_MS)
  doSomething()
}
suspend fun doSomethingAfter(){
    delay(DELAY_MS)
    doSomething()
}
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0"