Android에서 지연된 후 메소드를 호출하는 방법


769

지정된 지연 후에 다음 메소드를 호출 할 수 있기를 원합니다. 목표 c에는 다음과 같은 것이 있습니다.

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

java와 안드로이드 에서이 방법과 동등한 것이 있습니까? 예를 들어 5 초 후에 메소드를 호출 할 수 있어야합니다.

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

답변:


1858

코 틀린

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
이 솔루션은 UI 스레드에서만 유용합니다. 그렇지 않으면 일반 스레드에서, 당신은 내가 생각하는 가장 좋은 버전이 아닙니다 루퍼 구현해야
olivier_sdg

2
@olivier_sdg 왜 루퍼를 구현해야합니까?
djechlin

37
@djechlin 핸들러는 항상 루퍼에 연결되어야합니다. 루퍼는 post ()를 실행하는 Runnable을 실제로 처리합니다. UI 스레드에는 이미 Looper가 포함되어 있으므로 UI ​​스레드에서 새 Handler () 및 post () Runnables를 직접 만들 수 있습니다. 이 Runnable은 UI 스레드에서 실행됩니다. Runnables를 다른 스레드에서 실행하려면 새 스레드를 만든 다음 Looper.prepare (), 새 Handler () 및 Looper.loop ()를 만들어야합니다. 이 새 핸들러에 게시 된 모든 Runnable은이 새 스레드에서 실행됩니다. 이 작업을 모두 수행하지 않으면 post ()에서 예외가 발생합니다.
Dororo

12
경우에 당신은, 당신은 또한 할 수 필요가 취소 실행 가능한 호출하여 메시지 큐에 여전히만큼 실행을 removeCallbacks(Runnable r)Handler.
Dennis

9
SHOULDimport android.os.handler
카카

322

내 경우에는 다른 답변을 사용할 수 없습니다. 대신 기본 Java 타이머를 사용했습니다.

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

43
이것은 핸들러가 UI 스레드에서 실행되지 않을 때 루퍼 문제가 없기 때문에 핸들러를 사용하는 것보다 낫습니다.
벤 H

32
Android 문서에 따르면 "더 이상 필요하지 않은 경우 타이머를 취소하려면 타이머에 대한 참조를 유지해야합니다. 명시 적으로 취소되지 않은 타이머는 자원을 무기한 보유 할 수 있습니다. "
Pooks

14
주의! 이것은 UI 스레드에서 실행되지 않습니다. ui 스레드에서 이것을 실행하면 치명적인 오류가 발생했습니다. android.view.ViewRootImpl $ CalledFromWrongThreadException : 뷰 계층을 작성한 원래 스레드 만 해당 뷰를 만질 수 있습니다.
vovahost

13
@vovahost 타이머 타이머 내부의 UI 구성 요소를 업데이트하기 때문입니다
Tim

10
java.util.Timer (및 TimerTask)는 JDK 9에서 더 이상 사용되지 않습니다. TimerTask는 좋지 않은 작업에 대해 새 스레드를 작성합니다.
Varvara Kalinina 2016 년

183

참고 : 이 답변은 질문에 Android를 컨텍스트로 지정하지 않았을 때 제공되었습니다. Android UI 스레드와 관련된 답변 은 여기를 참조하십시오.


Mac OS API가 현재 스레드를 계속 사용하고 작업이 비동기 적으로 실행되도록 예약하는 것처럼 보입니다. Java에서는 동등한 기능이 java.util.concurrent패키지에서 제공됩니다 . 안드로이드가 어떤 제한을 부과하는지 잘 모르겠습니다.

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)메소드를 호출 하기 만하면 됩니다.
Dennis

이 답변은 구식이라고 생각합니다. .schedule은 더 이상 Runnable의 방법이 아닌 것 같습니다 ...? : /
beetree

5
@beetree의 메소드입니다 ScheduledExecutorService.
erickson

3
UI 스레드 객체가 관련된 경우에는 작동하지 않습니다. runOnUIThread (new runnable () {run () ....}); 또는 run () 내부에서 핸들러 객체를 사용하여 실행 파일을 게시합니다. {}
Jayant Arora

107

5 초 후에 UI 스레드에서 무언가를 실행하는 경우 :

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

8
이것이 looper.prepare에 대한 호출을 막고 모든 것을 UI 스레드에 배치하는 가장 좋은 솔루션인지 확인하십시오.
Tobliug

이것에 감사합니다, 루퍼 문제에 도움이되었습니다 :)
Tia

1
메인 루퍼에서 핸들러를 생성하는 데주의를 기울여야
하는데이

40

UIThread 내에서 Handler를 사용할 수 있습니다.

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 2016 년

24

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 누출은 개발자에게 별도로 이해하는 큰 주제입니다.
Khemraj

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있는 경우 UI 스레드에서 핸들러를 실행하는 데 사용할 수 있습니다 . 이렇게하면 호출 요청 예외에서 당신을 저장합니다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
이 방법은 최소 6 자 이상의 어리석은 SO 규칙으로 인해 게시물을 편집 할 수 없지만 '새 핸들러'다음에 '()'이없는 경우 '새 핸들러 ()'여야합니다.
Jonathan Muller

2
모든 것을 UI 스레드에 배치하는 대신 다음을 수행 할 수 있습니다. new Handler (Looper.getMainLooper ())
Tobliug

17

View.postDelayed()아래의 간단한 코드 를 사용하는 방법을 선호 합니다.

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

1
ui 요소 자체가 뷰 핸들러에서 예약되기 때문에 고정되지 않습니까?
JacksOnF110

1
아니요, 게시 된 작업은 1 초 안에 실행되지만이 두 번째 UI 스레드는 다른 유용한 작업을 수행합니다
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

당신이 사용하는 경우 안드로이드 스튜디오 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);

이것을 어떻게 취소 할 수 있습니까?
Damia Fuentes

나는 여기에 얼마나 많은 사람들이 Kotlin으로 행복하게 이동할지 놀랐지 만 표준 Java 인 Lambda Expressions를 완전히 무시합니다.
TomDK

6

Timer를 제안합니다 . 매우 특정 간격으로 메소드가 호출되도록 예약 할 수 있습니다. 이렇게하면 UI가 차단되지 않으며 메서드가 실행되는 동안 앱이 공명 상태를 유지합니다.

다른 옵션은 wait ()입니다. 메서드를 사용하면 지정된 시간 동안 현재 스레드를 차단합니다. UI 스레드에서이 작업을 수행하면 UI가 응답하지 않습니다.


2
Thread.sleep ()이 Object.wait ()보다 낫습니다. 대기는 알림을 받고 일부 활동을 동기화하고 있음을 나타냅니다. 수면은 단순히 지정된 시간 동안 아무것도하지 않기를 원한다는 것을 나타냅니다. 타이머는 나중에 특정 시점에 작업이 비동기 적으로 발생하도록하는 방법입니다.
Tim Bender

1
사실입니다. 그래서 다른 옵션으로 나열했습니다 ;-)
Nate

6

간단한 라인 핸들 포스트 지연의 경우 다음과 같이 할 수 있습니다.

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

이 고양이를 피부에 바르는 방법이 많으므로 여기에서 고려해야 할 사항이 몇 가지 있습니다. 답변이 모두 선택되어 선택되었지만. 나는 이것이 "주요 선정 된 간단한 답변"때문에 누군가가 잘못된 방향으로 나아가는 것을 피하기 위해 적절한 코딩 지침으로 다시 방문하는 것이 중요하다고 생각합니다.

먼저이 스레드에서 전체적으로 선정 된 답변 인 간단한 Post Delayed 답변에 대해 설명하겠습니다.

고려해야 할 몇 가지. 포스트 지연 후 메모리 누수, 죽은 객체, 수명주기 등이 발생할 수 있습니다. 따라서 올바르게 처리하는 것도 중요합니다. 몇 가지 방법으로이 작업을 수행 할 수 있습니다.

현대적인 발전을 위해 코 틀린에 공급하겠습니다

다음은 콜백에서 UI 스레드를 사용하고 콜백을 칠 때 액티비티가 여전히 살아 있는지 확인하는 간단한 예입니다.

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

이제 우리는 명백한 것을 이야기 했으므로 현대 코 루틴과 kotlin으로 깔끔한 옵션에 대해 이야기 해 봅시다. :). 아직 사용하지 않으면 실제로 누락 된 것입니다.

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

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

1
어떤 문제 라지브, 나는 한 걸음 더 나아가 그것을 수행하지 않고 라이브 데이터에게 코 루틴을 사용하여 라이프 사이클 인식과 자기 정리 호출을 피하기 위해 취소 될 수 있음을 언급하지만, 한 가지 대답에 너무 많은 학습 곡선을 던져하지 않을 것이다)

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
그런 기본 과제에 대한 고찰?
Max Ch

질문은 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);

3

Kotlin을 사용하여 다음을 수행하여 달성 할 수 있습니다.

Handler().postDelayed({
    // do something after 1000ms 
}, 1000)


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

또 다른 까다로운 방법은 다음과 같습니다. 실행 가능한 UI 요소 변경시 예외가 발생하지 않습니다.

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

안드로이드에서 적합한 솔루션 :

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
나는 do3 초의 지연 후 이렇게 생각했다
Manohar Reddy

0

안드로이드에서는 모든 함수의 지연 실행을 위해 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()
}
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.