关于Java:如何在Android中延迟调用方法

How to call a method after a delay in Android

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

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

在Android和Java中是否有这样的方法?例如,我需要能够在5秒钟后调用一个方法。

1
2
3
4
public void DoSomething()
{
     //do something here
}

科特林

1
2
3
Handler().postDelayed({
  //Do something after 100ms
}, 100)

爪哇

1
2
3
4
5
6
7
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
  @Override
  public void run() {
    //Do something after 100ms
  }
}, 100);


在我的案例中,我不能使用任何其他答案。我使用了原生Java计时器。

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


注意:这个答案是在问题没有指定android作为上下文时给出的。有关特定于Android UI线程的答案,请查看此处。

看起来macosapi允许当前线程继续运行,并安排任务异步运行。在Java中,等效函数由EDCOX1×0的封装提供。我不知道Android会有什么限制。

1
2
3
4
5
6
7
8
9
10
11
12
13
private static final ScheduledExecutorService worker =
  Executors.newSingleThreadScheduledExecutor();

void someMethod() {
  ?
  Runnable task = new Runnable() {
    public void run() {
      /* Do something… */
    }
  };
  worker.schedule(task, 5, TimeUnit.SECONDS);
  ?
}


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

1
2
3
4
5
6
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
    @Override
    public void run() {
        //Do something here
    }
}, 5000);


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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);

    }
});

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

1
2
3
4
5
6
7
8
9
10
11
12
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
    }
}


看到这个演示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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?");
     }
}

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

1
2
3
4
5
6
7
8
9
10
11
runOnUiThread(new Runnable() {
    @Override
    public void run() {
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                //Do something after 1 second
            }
        }, 1000);
    }
});

看起来很乱,但这是其中之一。


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

1
2
3
4
5
6
mView.postDelayed(new Runnable() {
    @Override
    public void run() {
        // Do something after 1000 ms
    }
}, 1000);


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

1
2
3
4
5
6
new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        //Do something after 100ms
    }
}, 100);

1
2
3
4
5
6
7
8
9
10
11
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);

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

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

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

1
2
3
4
5
Handler handler = new Handler();
handler.postDelayed(() -> callMyMethod(), 2000);

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


我建议使用计时器,它允许您在非常特定的时间间隔内调度要调用的方法。这不会阻塞您的用户界面,并且在方法执行期间保持应用程序的完整性。

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


Kotlin & Java Many Ways

1。使用Handler

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

2。使用定时任务

1
2
3
4
5
Timer().schedule(object : TimerTask() {
    override fun run() {
        TODO("Do something")
    }
}, 2000)

甚至更短

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

或者最短的

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

三。使用Executors

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

In Java

1。使用Handler

1
2
3
4
5
6
new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        //Do something
    }
}, 2000);

2。使用Timer

1
2
3
4
5
6
new Timer().schedule(new TimerTask() {          
    @Override
    public void run() {
        // Do something
    }
}, 2000);

三。使用ScheduledExecutorService

1
2
3
4
5
6
7
8
private static final ScheduledExecutorService worker = Executors.newSingleThreadScheduledExecutor();

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


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

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

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

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

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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");


使用CountDownTimer非常简单。有关更多详细信息,请访问https://developer.android.com/reference/android/os/countdowntimer.html

1
2
3
4
5
6
7
8
9
10
11
12
13
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();

所以这里有一些事情要考虑,因为有很多方法可以剥下这只猫的皮。尽管所有答案都已被选定。我认为,重要的是,重新审视这一点,使用适当的编码准则,以避免任何人仅仅因为"大多数人选择简单答案"而走错方向。

因此,首先让我们讨论一下简单的延迟后回答,它是这个线程中的赢家选择的答案。

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

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

下面是一个简单的例子,在回调中使用UI线程,并确认当您点击回调时,您的活动仍然有效。

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

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

1
2
3
4
5
6
7
8
9
10
11
    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上处理清理,这样它就不会命中回调。

1
2
3
4
    override fun onPause() {
        super.onPause()
        mHandler.removeCallbacks(null)
    }

既然我们已经讨论过了显而易见的问题,那么让我们来讨论一个更清洁的选择,它与现代的coroutines和kotlin一起使用。如果你还没有使用这些,你就真的错过了。

1
2
3
4
5
6
   fun doActionAfterDelay()
        launch(UI) {
            delay(MS_TO_DELAY)          
            actionToTake()
        }
    }

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

1
2
3
4
  fun doActionAfterDelay() = launch(UI){
      delay(MS_TO_DELAY)          
      actionToTake()
  }

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

1
2
3
4
5
6
7
8
9
10
11
12
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}")
            }
        }
   }
}

//处理清理

1
2
3
4
5
6
7
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)放入方法签名中,则可以在代码的调用行中分配作业。

因此,这个故事的寓意是要安全地处理延迟的行为,确保删除回调,或者取消工作,当然还要确认您有正确的生命周期来处理延迟回调完成时的项目。协同程序还提供可取消的操作。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
   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.lang.RuntimeException: Can't create handler inside thread that
has not called Looper.prepare()

1
2
3
4
5
6
7
final Handler handler = new Handler(Looper.getMainLooper());
handler.postDelayed(new Runnable() {
  @Override
  public void run() {
    //Do something after 100ms
  }
}, 100);

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

1
2
3
4
5
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.

这是科特林的答案,你这个懒惰的人:

1
2
3
Handler().postDelayed({
//doSomethingHere()
}, 1000)

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

1
2
3
4
5
6
new Handler().postDelayed(new Runnable() {
  @Override
  public void run() {
    //Do something after 100ms
  }
}, 100);

对于简单的线句柄post delay,可以执行以下操作:

1
2
3
4
5
6
new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        // Do someting
    }
}, 3000);

我希望这有帮助


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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) {

    }
}

可以这样调用动画:

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

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


在android中,我们可以在Kotlin代码下面编写延迟执行任何函数的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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()
}
}

Android中的合适解决方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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
        }
    }

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

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

类似的溶液,但使用更干净

把这个函数写在类的外面

1
2
3
4
5
fun delay(duration: Long, `do`: () -> Unit) {

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

}

用途:

1
2
3
delay(5000) {
    //Do your work here
}