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


770

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

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

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

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

Ответы:


1860

Котлин

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


Ява

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



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

2
@olivier_sdg, почему вам нужно реализовать Looper?
Джечлин

37
@djechlin Обработчик всегда должен быть связан с Looper, который фактически обрабатывает Runnable, который вы публикуете (). Поток пользовательского интерфейса уже поставляется с Looper, так что вы можете просто создать новый Handler () в потоке пользовательского интерфейса и напрямую запустить в него Runnables. Эти Runnables выполняются в потоке пользовательского интерфейса. Чтобы Runnables выполнялись в другом потоке, вам нужно создать новый поток, затем Looper.prepare (), создать новый Handler () и затем Looper.loop (). Все Runnables, опубликованные в этом новом обработчике, будут выполняться в этом новом потоке. Если вы не сделаете все это, post () выдаст исключение.
Дороро

12
В случае , если вам нужно, вы можете также отменить выполнение до тех пор , как Runnable все еще находится в очереди сообщений, позвонив removeCallbacks(Runnable r)на Handler.
Деннис

9
долженimport android.os.handler
КаКа

322

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

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

43
это лучше, чем те, которые используют Handler, потому что у него нет проблем с Looper, когда Handler не запускается в потоке пользовательского интерфейса.
Бен Х

32
Вы должны сохранить ссылку на свой таймер, чтобы отменить его, когда он больше не нужен, поскольку в соответствии с документацией для Android: «Когда таймер больше не нужен, пользователи должны вызывать метод cancel (), который освобождает поток таймера и другие ресурсы. Таймеры, не отмененные явным образом, могут хранить ресурсы неограниченное время ».
Pooks

14
Внимание! Это не работает в потоке пользовательского интерфейса. Выполнение этого в потоке пользовательского интерфейса вызвало фатальную ошибку: android.view.ViewRootImpl $ CalledFromWrongThreadException: только исходный поток, создавший иерархию представлений, может касаться его представлений.
vovahost

13
@vovahost, это только потому, что вы обновляете компоненты пользовательского интерфейса внутри блока таймера
Тим

10
Обратите внимание, что java.util.Timer (и TimerTask) будут устаревшими в JDK 9. TimerTask создает новые потоки для задач, что не очень хорошо.
Варвара Калинина

183

Примечание. Этот ответ был дан, когда в вопросе не был указан 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);
  
}

3
Это никогда не называет мне Runnable
Supuhstar

14
В качестве примечания: это также позволяет позже отменить задачу, что может быть полезно в некоторых ситуациях. Просто сохраните ссылку на ScheduledFuture<?>возвращаемый объект worker.schedule()и вызовите его cancel(boolean)метод.
Деннис,

Я думаю, что этот ответ устарел. .schedule, похоже, больше не является методом Runnable ...? : /
свекла

5
@beetree это метод на ScheduledExecutorService.
Эриксон

3
Это не работает, если участвуют объекты потока пользовательского интерфейса, вы должны вызвать runOnUIThread (new runnable () {run () ....}); или опубликовать исполняемый объект с использованием объекта-обработчика изнутри run () {}
Jayant Arora

107

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

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

8
Подтвердите, что это лучшее решение, чтобы предотвратить вызов looper.prepare и поместить все это в поток пользовательского интерфейса.
Тоблюг

Спасибо за это, помогли мне с проблемами Looper :)
Tia

1
Я был бы осторожен с созданием обработчика на главном петлителе, тогда в этой теме не нужно выполнять долгую задачу
Shayan_Aryan

40

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

    }
});

36

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

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

Это нормально, если я использую этот подход, чтобы иметь обратную связь по нажатию элемента .. view.setColor (some_color), а затем удалить этот цвет в обработчике через x секунд ...?
eRaisedToX

25

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

1
@JanRabe Спасибо за ваше предложение. Я ценю это. Однако вопрос в том How to call a method after a delay in Android. Поэтому я сосредоточился на этом. К точке. В противном случае утечка Java - большая тема для отдельного понимания разработчиками.
Хемрай

20

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

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

20

Если вам нужно использовать обработчик, но вы находитесь в другом потоке, вы можете использовать его 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 символами для редактирования, но после «нового обработчика» отсутствует «()», это должен быть «новый обработчик ()»
Джонатан Мюллер

2
Вместо того, чтобы помещать все в поток пользовательского интерфейса, вы можете сделать: новый обработчик (Looper.getMainLooper ())
Tobliug

17

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

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

1
Не замораживает ли он сам элемент пользовательского интерфейса, потому что он будет запланирован в обработчике представлений?
JacksOnF1re

1
Нет, опубликованное задание будет выполнено в течение 1 секунды, но в течение этого второго потока пользовательского интерфейса выполняет другую полезную работу
demaksee

14

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

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

10

Если вы используете 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);

Как мы можем отменить это?
Дамия Фуэнтес

Я удивлен, как многие здесь с радостью перейдут на Kotlin, но при этом полностью игнорируют лямбда-выражения, которые являются стандартной Java.
TomDK

6

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

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


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

1
Это правда. Вот почему я перечислил это как другой вариант ;-)
Nate

6

Для простой строки Handle Post delay вы можете сделать следующее:

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

надеюсь, это поможет


5

Вы можете использовать это для простейшего решения:

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

5

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

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

5

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

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

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

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

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

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

или если вы хотите всегда запускать пользовательский интерфейс для этого метода, вы можете просто сделать:

  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 сопрограммы могут быть осведомлены о жизненном цикле и самоотменяться, чтобы избежать вызовов очистки, но не хотят бросать слишком много кривых обучения в один ответ;)
Сэм

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
Отражение для такой основной задачи?
Макс. Ч

Поскольку вопрос вызова метода аналогичен iOS performSelector. это лучший способ сделать.
HelmiB

3

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

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

Это очень легко с помощью 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();

2

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

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

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

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



1

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

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

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

0

Аналогичное решение, но гораздо более чистое в использовании

Напишите эту функцию вне класса

fun delay(duration: Long, `do`: () -> Unit) {

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

}

Применение:

delay(5000) {
    //Do your work here
}

Что значит "делать"?
Skizo-ozᴉʞS

просто имя, держи что-нибудь там. doэто встроенный метод, поэтому мы должны использовать `, чтобы использовать его в качестве имени переменной
Manohar Reddy

Спасибо, но почему используется это имя переменной? Я имею в виду, какова функция этого.
Skizo-ozᴉʞS

1
Я думал так doпосле задержки 3 секунды
Манохар Редди

0

В Android ниже можно написать код kotlin для задержки выполнения любой функции.

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()
}
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.