Как вызвать метод после задержки в Android

663

Я хочу иметь возможность вызвать следующий метод после указанной задержки.  В объективе c было что-то вроде:

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

Есть ли эквивалент этого метода в android с java? Например, мне нужно иметь возможность вызывать метод через 5 секунд.

public void DoSomething()
{
     //do something here
}
Теги:
handler
delay

26 ответов

1662
Лучший ответ

Лучшая версия:

final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
  @Override
  public void run() {
    //Do something after 100ms
  }
}, 100);
  • 0
    Это отлично работало и вписывалось прямо в метод, из которого мне нужно было сделать вызов. Благодарю.
  • 0
    Тоже самое; используйте его для задержки фиксации фрагмента для улучшения анимации.
Показать ещё 22 комментария
290

Я не мог использовать какие-либо другие ответы в моем случае. Вместо этого я использовал собственный таймер Java.

new Timer().schedule(new TimerTask() {          
    @Override
    public void run() {
        // this code will be executed after 2 seconds       
    }
}, 2000);
  • 37
    это лучше, чем те, которые используют Handler, потому что у него нет проблем с Looper, когда Handler не запускается в потоке пользовательского интерфейса.
  • 29
    Вы должны сохранить ссылку на свой таймер, чтобы отменить его, когда он больше не нужен, поскольку в соответствии с документацией для Android: «Когда таймер больше не нужен, пользователи должны вызвать метод cancel (), который освобождает поток таймера и другие ресурсы. Таймеры, не отмененные явным образом, могут хранить ресурсы неограниченное время ».
Показать ещё 7 комментариев
171

Примечание. Этот ответ был дан, когда вопрос не указывал на Android как контекст. Для ответа, характерного для потока пользовательского интерфейса Android смотрите здесь.


Похоже, что 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);
  ⋮
}
  • 3
    Это никогда не вызывает Runnable для меня
  • 14
    В качестве примечания: это также позволяет позднее отменить задачу, что может быть полезно в некоторых ситуациях. Просто сохраните ссылку на ScheduledFuture<?> , worker.schedule() и вызовите его метод cancel(boolean) .
Показать ещё 7 комментариев
90

За выполнение чего-то в потоке пользовательского интерфейса через 5 секунд:

new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
    @Override
    public void run() {
        //Do something here
    }
}, 5000);
  • 6
    Подтвердите, что это лучшее решение для предотвращения вызова looper.prepare и помещения всего этого в поток пользовательского интерфейса.
  • 0
    Спасибо за это, помогли мне с проблемами Looper :)
Показать ещё 1 комментарий
37

Спасибо за все замечательные ответы, я нашел решение, которое наилучшим образом соответствует моим потребностям.

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
    }
}
  • 0
    Это нормально, если я использую этот подход, чтобы иметь обратную связь по нажатию элемента .. view.setColor (some_color), а затем удалить этот цвет в обработчике через x секунд ...?
36

вы можете использовать Handler внутри 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);

    }
});
22

Если вам нужно использовать Handler, но вы используете другой поток, вы можете использовать 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);
    }
});

Выглядит довольно грязно, но это один из способов.

  • 4
    Это работает, я не могу редактировать ваш пост из-за глупых правил SO с минимум 6 символами для редактирования, но отсутствует «()» после «нового обработчика», это должно быть «новый обработчик ()»
  • 0
    @ Корен, спасибо за указание на ошибку. Я исправил это.
Показать ещё 1 комментарий
22

Смотрите это демо:

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

Я предпочитаю использовать View.postDelayed(), простой код ниже:

mView.postDelayed(new Runnable() {
    @Override
    public void run() {
        // Do something after 1000 ms
    }
}, 1000);
  • 1
    Не замораживает ли он сам элемент пользовательского интерфейса, потому что он будет запланирован в обработчике представлений?
  • 1
    Нет, опубликованная задача будет выполнена за 1 секунду, но в течение этого второго потока пользовательского интерфейса выполняет другую полезную работу
13

Вот мое кратчайшее решение:

new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        //Do something after 100ms
    }
}, 100);
10
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); 
7

Если вы используете Android Studio 3.0 и выше, вы можете использовать лямбда-выражения. Метод callMyMethod() вызывается через 2 секунды:

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);
  • 0
    Как мы можем отменить это?
  • 0
    @DamiaFuentess Смотрите мой обновленный ответ.
6

Я предлагаю Timer, он позволяет планировать метод, который будет вызываться на очень конкретном интервале. Это не будет блокировать ваш пользовательский интерфейс и не будет поддерживать ваше приложение во время выполнения метода.

Другим вариантом является метод wait();, который блокирует текущий поток в течение указанного периода времени. Это приведет к тому, что ваш пользовательский интерфейс перестанет отвечать, если вы делаете это в потоке пользовательского интерфейса.

  • 2
    Thread.sleep () лучше, чем Object.wait (). Ожидание означает, что вы ожидаете получать уведомления и синхронизируете некоторые действия. Сон означает, что вы просто хотите ничего не делать в течение определенного времени. Таймер - это путь, если вы хотите, чтобы действие происходило асинхронно в какой-то более поздний момент времени.
  • 1
    Это правда. Вот почему я перечислил это как другой вариант ;-)
5

Вы можете использовать это для Simplest Solution:

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
4

Вы можете сделать его более чистым, используя недавно введенные лямбда-выражения:

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

Kotlin & Java много способов

1. Использование Handler

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

2. Использование TimerTask

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)

На яве

1. Использование Handler

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

2. Использование Timer

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

Я создал более простой метод для вызова этого.

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

  • 3
    Отражение для такой основной задачи?
  • 0
    Так как вопрос вызова метода, аналогичного iOS, performSelector . это лучший способ сделать.
2

Ниже один работает, когда вы получаете,

java.lang.RuntimeException: не может создать обработчик внутри потока, который не вызвал Looper.prepare()

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

Поэтому есть несколько вещей, которые следует учитывать здесь, поскольку существует так много способов скинуть этот кот. Хотя ответы уже были выбраны и выбраны. Я думаю, что важно, чтобы это пересматривалось с правильными инструкциями по кодированию, чтобы избежать того, чтобы кто-то шел в неправильном направлении только из-за "большинства выбранных простых ответов".

Поэтому сначала давайте обсудим простой ответ "Отложенная почта", который является победителем, выбранным в целом в этом потоке.

Несколько вещей, чтобы рассмотреть. После задержки сообщения вы можете столкнуться с утечками памяти, мертвыми объектами, оставшимися жизненными циклами и т.д. Поэтому правильное обращение с ним также важно. Вы можете сделать это несколькими способами.

Ради современного развития, я поставлю в КОТЛИН

Вот простой пример использования потока пользовательского интерфейса для обратного вызова и подтверждения того, что ваша активность все еще жива и работоспособна, когда вы нажимаете обратный вызов.

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

Теперь, когда мы поговорили через очевидное, расскажите о более чистом варианте с современными деньгами и котлин :). Если вы еще не используете их, вы действительно упускаете.

   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) в подпись метода, задание может быть назначено в вызывающей строке кода.

так что мораль этой истории должна быть безопасной с вашими отложенными действиями, убедитесь, что вы удалили свои обратные вызовы или отменили свои задания, и, конечно же, подтвердите, что у вас есть правильный жизненный цикл, чтобы коснуться предметов в вашем обратном вызове задержки. 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}")
            }
        }
  • 1
    Нет проблем, Раджив, я бы сделал еще один шаг вперед и упомянул, что при использовании Live Data сопрограммы могут быть осведомлены о жизненном цикле и самоотменяться, чтобы избежать вызовов очистки, но не хотят бросать слишком много кривых обучения в один ответ;)
2

Это очень просто, используя 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();
1

Я люблю вещи чище: вот моя реализация, встроенный код для использования внутри вашего метода

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

Вот ответ в Котлин вам ленивые, ленивые люди:

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

все, похоже, забывают очистить обработчик, прежде чем публиковать на нем новую версию или сообщение. В противном случае они могут потенциально накапливаться и вызывать плохое поведение.

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

Вот еще один сложный способ: он не будет генерировать исключение, когда элементы пользовательского интерфейса с изменяемым изменением.

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

Анимация может быть прикреплена к любому виду.

0

Если вы используете RxAndroid, то обработка потоков и ошибок станет намного проще. Следующий код выполняется с задержкой

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

Подходящее решение в 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
        }
    }

Ещё вопросы

Сообщество Overcoder
Наверх
Меню