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


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

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

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

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

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

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

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

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

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

ради современного развития, я поставлю в Котлин

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

  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)