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

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

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

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

当前回答

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

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

其他回答

谢谢你的回答,我找到了一个最适合我需要的解决方案。

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

我建议使用定时器,它允许你在特定的时间间隔内安排一个方法被调用。这将不会阻塞你的UI,并保持你的应用程序的共鸣,而方法正在执行。

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

你可以使用新引入的lambda表达式让它更简洁:

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

所以这里有几件事要考虑,因为有这么多方法来剥这只猫的皮。尽管答案都已经给出了。我认为重要的是,要用适当的编码指南重新审视这个问题,以避免任何人仅仅因为“多数选择的简单答案”而走向错误的方向。

因此,首先让我们讨论一下简单的Post Delayed answer,这是在这个线程中胜出的答案。

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

为了现代发展,我将在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.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}")
            }
        }

在我的例子中,我不能用其他答案。 我使用本机java定时器代替。

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