Как вызвать метод после задержки в 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.
еще, ниже может быть другое чистое полезное решение:
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.htmlimport 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));
анимация может прикрепляться к любому виду.
так что есть несколько вещей, чтобы рассмотреть здесь, как есть так много способов, чтобы освежевать эту кошку. Хотя ответы на все уже были даны отборные и избранные. Я думаю, что важно, чтобы это было пересмотрено с правильными рекомендациями по кодированию, чтобы избежать любого движения в неправильном направлении только из-за "большинства выбранных простых ответов".
Итак, сначала давайте обсудим простой пост отложенный ответ, который является победителем выбранного ответа в целом в этой теме.
пару вещи, чтобы рассмотреть. После задержки сообщения Вы можете столкнуться с утечками памяти, мертвыми объектами, жизненными циклами, которые ушли, и многое другое. Поэтому правильное обращение с ним также важно. Вы можете сделать это несколькими способами.
ради современного развития, я поставлю в Котлин
вот простой пример использования потока пользовательского интерфейса для обратного вызова и подтверждения того, что ваша активность все еще жива и хорошо, когда вы нажмете свой обратный вызов.
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) в сигнатуру метода, задание может быть назначено в вызове строка кода.
таким образом, мораль истории заключается в том, чтобы быть в безопасности с вашими отложенными действиями, убедитесь, что вы удаляете свои обратные вызовы или отменяете свои задания и, конечно же, подтверждаете, что у вас есть правильный жизненный цикл, чтобы коснуться элементов на вашем обратном вызове с задержкой. Сопрограммы также предлагают отменяемые действия.
также стоит отметить, что вы обычно должны обрабатывать различные исключения, которые могут поставляться с сопрограммами. Например, отмена, исключение, тайм-аут, что бы вы ни решили использовать. Вот более сложный пример, если вы решили действительно начать использовать сопрограммы.
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)