Как вызвать метод после задержки в Android
Я хочу иметь возможность вызвать следующий метод после указанной задержки. В objective c было что-то вроде:
[self performSelector:@selector(DoSomething) withObject:nil afterDelay:5];
есть ли эквивалент этого метода в android с java? Например, мне нужно иметь возможность вызывать метод через 5 секунд.
public void DoSomething()
{
//do something here
}
22 ответов
лучше вариант:
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
//Do something after 100ms
}
}, 100);
Я не мог использовать ни один из других ответов в моем случае. Вместо этого я использовал собственный таймер java.
new Timer().schedule(new TimerTask() {
@Override
public void run() {
// this code will be executed after 2 seconds
}
}, 2000);
Примечание: этот ответ был дан, когда вопрос не указывал Android в качестве контекста. Для ответа, специфичного для потока пользовательского интерфейса Android посмотреть здесь.
похоже, что API Mac OS позволяет текущему потоку продолжаться и планирует выполнение задачи асинхронно. В 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);
⋮
}
для выполнения чего-то в потоке пользовательского интерфейса через 5 секунд:
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
@Override
public void run() {
//Do something here
}
}, 5000);
вы можете использовать обработчик внутри 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);
}
});
Спасибо за все отличные ответы, я нашел решение, которое лучше всего соответствует моим потребностям.
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
}
}
Если вам нужно использовать обработчик, но вы находитесь в другом потоке, вы можете использовать runonuithread
запустить обработчик в потоке пользовательского интерфейса. Это избавит вас от исключений, брошенных с просьбой позвонить Looper.Prepare()
runOnUiThread(new Runnable() {
@Override
public void run() {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
//Do something after 1 second
}
}, 1000);
}
});
выглядит довольно сумбурно, но это один из способов.
смотрите эту демонстрацию:
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?");
}
}
Я предпочитаю использовать View.postDelayed()
способ, простой код ниже:
mView.postDelayed(new Runnable() {
@Override
public void run() {
// Do something after 1000 ms
}
}, 1000);
вот мое самое короткое решение:
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
//Do something after 100ms
}
}, 100);
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 и выше, вы можете использовать лямбда-выражения. Метод callMyMethod()
вызывается через 2 секунды:
new Handler().postDelayed(() -> callMyMethod(), 2000);
в случае, если вам нужно отменить все отложенные runnables:
Handler handler = new Handler();
handler.postDelayed(() -> callMyMethod(), 2000);
// When you need to cancel all your posted runnables just use:
handler.removeCallbacksAndMessages(null);
вы можете использовать это для простейшего решения:
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
//Write your code here
}
}, 5000); //Timer is in ms here.
Else, ниже может быть другое чистое полезное решение:
new Handler().postDelayed(() ->
{/*Do something here*/},
5000); //time in ms
Я создал более простой метод для вызова этого.
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");
вы можете сделать его намного чище, используя недавно введенные лямбда-выражения:
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 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();
все, кажется, забывают очистить обработчик перед публикацией нового 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.
вот еще один сложный способ: он не будет вызывать исключение, когда выполняемые элементы пользовательского интерфейса изменяются.
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));
анимация может прикрепляться к любому виду.
так что есть несколько вещей, чтобы рассмотреть здесь, как есть так много способов освежевать эту кошку. Хотя ответы на все вопросы уже даны и выбраны. Я думаю, что важно, чтобы это было пересмотрено с надлежащими руководящими принципами кодирования, чтобы избежать кого-либо, идущего в неправильном направлении только из-за "большинства выбранных простых ответов".
Итак, сначала давайте обсудим простой ответ с задержкой, который является победителем выбранного ответа в целом в этой теме.
пару есть над чем подумать. После задержки post вы можете столкнуться с утечками памяти, мертвыми объектами, жизненными циклами, которые ушли, и многое другое. Поэтому правильное обращение с ним также важно. Вы можете сделать это несколькими способами.
ради современного развития, я поставлю в Котлин
вот простой пример использования потока пользовательского интерфейса при обратном вызове и подтверждения того, что ваша деятельность все еще жива и здорова, когда вы нажимаете обратный вызов.
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)
}
теперь, когда мы обсудили очевидное, давайте поговорим о более чистом варианте с современными coroutines и kotlin:). Если вы еще не используете их, вам действительно не хватает из.
fun doActionAfterDelay()
launch(UI) {
delay(MS_TO_DELAY)
actionToTake()
}
}
или если вы хотите всегда запускать пользовательский интерфейс по этому методу, вы можете просто сделать:
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}")
}
}
}
}
/ / handle cleanup
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
}
}
если вы поместите launch (UI) в подпись метода, задание может быть назначено в вызове строка кода.
таким образом, мораль истории заключается в том, чтобы быть в безопасности с задержкой действий, убедитесь, что вы удалите свои обратные вызовы, или отменить работу и, конечно, подтвердить, что у вас есть правильный жизненный цикл, чтобы коснуться пунктов на задержке обратного вызова завершена. Coroutines также предлагает отменяемые действия.
также стоит отметить, что вы обычно обрабатывать различные исключения, которые могут прийти с сопрограммами. Например, отмена, исключение, тайм-аут, все, что вы решите использовать. Вот более сложный пример, если вы решили действительно начать использовать сопрограммы.
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}")
}
}
подходящее решение в 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
}
}
вот ответ: в Котлине вы ленивые, ленивые люди:
Handler().postDelayed({
//doSomethingHere()
}, 1000)