정의 된 간격으로 Android에서 Runnable 스레드를 실행하는 방법은 무엇입니까?


351

Android 에뮬레이터 화면에 일정한 간격으로 일부 텍스트를 표시하는 응용 프로그램을 개발했습니다. 나는 Handler수업을 사용하고 있습니다. 내 코드의 스 니펫은 다음과 같습니다.

handler = new Handler();
Runnable r = new Runnable() {
    public void run() {
        tv.append("Hello World");               
    }
};
handler.postDelayed(r, 1000);

이 응용 프로그램을 실행하면 텍스트가 한 번만 표시됩니다. 왜?


109
나는 실행 가능한 방법을 기억할 수 없다, 그래서 나는 항상 그것을 수행하는 방법에 대한 귀하의 게시물을 방문 :))
Adrian Sicaru

2
하하 여기에 같은 친구 너무 진실
NoXSaeeD

1
람다는 이제 대부분의 시간을가는 길이다;)
Xerus

@AdrianSicaru : 동일
Sovandara LENG

답변:


534

귀하의 예에 대한 간단한 해결책은 다음과 같습니다.

handler = new Handler();

final Runnable r = new Runnable() {
    public void run() {
        tv.append("Hello World");
        handler.postDelayed(this, 1000);
    }
};

handler.postDelayed(r, 1000);

또는 예를 들어 (원래 러너와 함께) 일반 스레드를 사용할 수 있습니다.

Thread thread = new Thread() {
    @Override
    public void run() {
        try {
            while(true) {
                sleep(1000);
                handler.post(this);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
};

thread.start();

실행 가능 오브젝트를 실행을 위해 메시지 큐로 송신 될 수있는 명령으로 간주하고 해당 명령을 송신하는 데 사용되는 헬퍼 오브젝트로 핸들러를 처리 할 수 ​​있습니다.

자세한 내용은 여기 http://developer.android.com/reference/android/os/Handler.html


Alex, 나는 작은 의심을 가지고 있습니다. 이제 스레드가 완벽하게 실행되고 텍스트를 연속적으로 표시합니다.
Rajapandian 2009

11
부울 변수 _stop을 정의하고 중지하려는 경우 'true'로 설정할 수 있습니다. 그리고 'while (true)'를 'while (! _ stop)'로 변경하거나 첫 번째 샘플을 사용한 경우 'if (! _ stop) handler.postDelayed (this, 1000)'으로 변경하십시오.
alex2k8 2009

메시지를 다시 시작하려면 어떻게합니까?
Sonhja

8 개의 서로 다른 ImageView를 차례로 표시 할 수있는 실행 파일이 필요한 경우 동일한 방식으로 보이지 않게 설정 ( "깜박임"애니메이션 생성)하려면 어떻게해야합니까?
Droidman December

1
Handler가 기본 스레드에 첨부되도록하려면 다음과 같이 초기화해야합니다. handler = new Handler (Looper.getMainLooper ());
Yair Kukielka

47
new Handler().postDelayed(new Runnable() {
    public void run() {
        // do something...              
    }
}, 100);

2
Handler가 기본 스레드에 첨부되도록하려면 다음과 같이 초기화해야합니다. new Handler (Looper.getMainLooper ());
Yair Kukielka

1
이 솔루션이 원래 게시물과 동일하지 않습니까? 100 밀리 초 후에 한 번만 Runnable을 실행합니다.
tronman

@ YairKukielka 답변이 해결책입니다! MainLooper를 연결해야합니다. 그런 구세주!
Houssem Chlegou

40

나는 매초마다 올바른 업데이트를 위해 Alex2k8의 첫 번째 솔루션을 향상시킬 수 있다고 생각합니다.

1. 원본 코드 :

public void run() {
    tv.append("Hello World");
    handler.postDelayed(this, 1000);
}

2. 분석

  • 상기 비용에서, 디스플레이 된 500 배 지연 시간이 500 * T 밀리 초인 후에 tv.append("Hello Word")비용 T 밀리 초를 가정한다
  • 장시간 실행하면 지연이 증가합니다

3. 해결책

지연을 피하려면 postDelayed () 순서를 변경하십시오.

public void run() {
    handler.postDelayed(this, 1000);
    tv.append("Hello World");
}

6
-1 당신이 run ()에서 수행하는 작업이 각 실행에 일정한 양의 비용이 있다고 가정하고, 이것이 동적 데이터 (일반적으로)에 대한 작업이라면 다음에서 발생하는 하나 이상의 run ()으로 끝납니다. 한번. postDelayed가 일반적으로 끝에 배치되는 이유입니다.
Jay

1
@Jay 불행하게도 당신은 틀 렸습니다. 핸들러는 단일 스레드 (및 해당 스레드의 실행 메소드 인 루퍼) + MessageQueue와 연관됩니다. 메시지를 게시 할 때마다 메시지를 대기열에 넣고 다음에 루 퍼가 대기열을 확인할 때 게시 한 Runnable의 run 메소드를 실행합니다. 그것이 하나의 스레드에서만 발생하기 때문에 동시에 둘 이상을 실행할 수 없습니다. 또한 postDelayed를 먼저 수행하면 내부적으로 현재 시간 + 1000을 실행 시간으로 사용하기 때문에 실행 당 1000ms에 가깝습니다. 게시물 앞에 코드를 넣으면 추가 지연이 추가됩니다.
zapl

1
@zapl 핸들러에 대한 팁 덕분에 여러 개의 실행 파일과 여러 스레드를 실행한다고 가정했습니다. 내부적으로 실행 시간이 1000ms보다 작거나 같으면 if ((현재 시간-lastruntime)> 1000)과 같은 조건이 정상적으로 작동하지만이 시간을 초과하면 비선형 간격으로 타이머가 발생합니다. 실행 방법의 실행 시간에 전적으로 의존 (따라서 예측할 수없는 계산 비용에 대한 요점)
Jay

고정 된 기간을 원한다면 충돌없이, 작업을 시작하기 전에 시작 시간을 측정하고 이에 따라 지연을 조정하십시오. CPU가 사용 중이면 여전히 약간의 대기 시간이 표시되지만 더 엄격한 기간을 허용하고 시스템 과부하가 발생했는지 감지 할 수 있습니다 (아마도 우선 순위가 낮은 항목에 백 오프).
Ajax

27

반복 작업을 위해 사용할 수 있습니다

new Timer().scheduleAtFixedRate(task, runAfterADelayForFirstTime, repeaingTimeInterval);

처럼 불러

new Timer().scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {

            }
        },500,1000);

위의 코드는 0.5 초 후에 처음으로 실행 되며 매 마다 반복됩니다 (1000)

어디

태스크 는 실행될 메소드입니다.

최초 실행 시간

( 실행을 반복하는 시간 간격 )

둘째로

작업을 여러 번 실행하려는 경우 CountDownTimer 를 사용할 수도 있습니다 .

    new CountDownTimer(40000, 1000) { //40000 milli seconds is total time, 1000 milli seconds is time interval

     public void onTick(long millisUntilFinished) {
      }
      public void onFinish() {
     }
    }.start();

//Above codes run 40 times after each second

그리고 당신은 또한 runnable로 할 수 있습니다. 같은 실행 가능한 방법을 만듭니다

Runnable runnable = new Runnable()
    {
        @Override
        public void run()
        {

        }
    };

그리고이 두 가지 방법으로 부르십시오

new Handler().postDelayed(runnable, 500 );//where 500 is delayMillis  // to work on mainThread

또는

new Thread(runnable).start();//to work in Background 

옵션 3의 경우 어떻게 일시 중지 / 다시 시작하고 영구적으로 중지 할 수 있습니까?
Si8

Handler handler = new Handler ()와 같은 Handler의 인스턴스를 만들고 handler.removeCallbacksAndMessages (null)과 같이 제거합니다.
Zar E Ahmer

24

나는이 전형적인 경우, 즉 일정한 간격으로 무언가를 실행하는 Timer것이 더 적합하다고 생각합니다. 다음은 간단한 예입니다.

myTimer = new Timer();
myTimer.schedule(new TimerTask() {          
@Override
public void run() {
    // If you want to modify a view in your Activity
    MyActivity.this.runOnUiThread(new Runnable()
        public void run(){
            tv.append("Hello World");
        });
    }
}, 1000, 1000); // initial delay 1 second, interval 1 second

사용하면 Timer몇 가지 장점이 있습니다.

  • 초기 지연 및 간격은 schedule함수 인수
  • 타이머는 간단하게 호출하여 중지 할 수 있습니다 myTimer.cancel()
  • 하나의 스레드 만 실행하려면 새 스레드 myTimer.cancel() 예약 하기 전에 호출 해야합니다 (myTimer가 null이 아닌 경우).

7
나는 안드로이드 라이프 사이클을 고려하지 않기 때문에 타이머가 더 적절하다는 것을 믿지 않습니다. 일시 중지했다가 다시 시작하면 타이머가 올바르게 실행될 것이라는 보장이 없습니다. 나는 실행 가능한 것이 더 나은 선택이라고 주장합니다.
Janpan

1
앱이 백그라운드에 배치되면 핸들러가 일시 중지됨을 의미합니까? 그리고 초점이 회복되면 아무 일도 없었던 것처럼 계속 될 것입니까?
Andrew Gallasch

17
Handler handler=new Handler();
Runnable r = new Runnable(){
    public void run() {
        tv.append("Hello World");                       
        handler.postDelayed(r, 1000);
    }
}; 
handler.post(r);

5
오류가 발생합니다. 두 번째 줄에서는 r아직 정의 된 변수 를 호출합니다 .
서울

Handler가 기본 스레드에 첨부되도록하려면 다음과 같이 초기화해야합니다. handler = new Handler (Looper.getMainLooper ());
Yair Kukielka

반복적 인 답변!
Hamid

이미지 뷰 클릭으로 런너 블을 일시 중지 / 재개하려면 어떻게해야합니까?
Si8

4

Handler.post () 메소드의 문서를 올바르게 이해하면 :

Runnable r이 메시지 큐에 추가되도록합니다. 실행 가능 파일은이 핸들러가 연결된 스레드에서 실행됩니다.

따라서 @ alex2k8에서 제공하는 예제는 올바르게 작동하지만 동일하지 않습니다. where Handler.post()가 사용되는 경우 새 스레드가 작성되지 않습니다 . 당신 은 EDT에 의해 실행될 Runnable스레드에 게시 합니다 . 그 후에는 EDT 만 실행됩니다HandlerRunnable.run() 다른 하지 않습니다.

기억하십시오 : Runnable != Thread.


1
사실입니다. 항상 새 스레드를 만들지 마십시오. 핸들러 및 기타 실행 풀의 요점은 스레드 작성 및 GC를 피하기 위해 하나 또는 두 개의 스레드가 큐에서 태스크를 가져 오는 것입니다. 실제로 유출되는 앱이 있다면 여분의 GC가 OutOfMemory 상황을 해결하는 데 도움이 될 수 있지만 두 경우 모두 더 나은 솔루션은 필요한 것보다 많은 작업을 생성하지 않는 것입니다.
Ajax

그래서 이것을하는 더 좋은 방법은 alex2k8의 대답에 따라 일반 스레드를 사용하는 것입니까?
Compaq LE2202x

4

코 틀린

private lateinit var runnable: Runnable
override fun onCreate(savedInstanceState: Bundle?) {
    val handler = Handler()
    runnable = Runnable {
        // do your work
        handler.postDelayed(runnable, 2000)
    }
    handler.postDelayed(runnable, 2000)
}

자바

Runnable runnable;
Handler handler;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    handler = new Handler();
    runnable = new Runnable() {
        @Override
        public void run() {
            // do your work
            handler.postDelayed(this, 1000);
        }
    };
    handler.postDelayed(runnable, 1000);
}

1

흥미로운 예는 별도의 스레드에서 카운터 / 스톱워치를 계속 볼 수 있다는 것입니다. GPS 위치도 표시합니다. 주요 활동 동안 사용자 인터페이스 스레드가 이미 있습니다.

발췌 :

try {    
    cnt++; scnt++;
    now=System.currentTimeMillis();
    r=rand.nextInt(6); r++;    
    loc=lm.getLastKnownLocation(best);    

    if(loc!=null) { 
        lat=loc.getLatitude();
        lng=loc.getLongitude(); 
    }    

    Thread.sleep(100); 
    handler.sendMessage(handler.obtainMessage());
} catch (InterruptedException e) {   
    Toast.makeText(this, "Error="+e.toString(), Toast.LENGTH_LONG).show();
}

코드를 보려면 여기를 참조하십시오.

주요 활동의 사용자 인터페이스 스레드와 함께 실행 가능한 GPS 위치 및 현재 시간을 표시하는 스레드 예제


1
힌트 : 답변을 유용하게하려면 여기에서 입력 형식을 지정하십시오. 그 미리보기 창은 이유가 있습니다.
GhostCat

0

이제 Kotlin에서는 다음과 같이 스레드를 실행할 수 있습니다.

class SimpleRunnable: Runnable {
    public override fun run() {
        println("${Thread.currentThread()} has run.")
    }
}
fun main(args: Array<String>) {
    val thread = SimpleThread()
    thread.start() // Will output: Thread[Thread-0,5,main] has run.
    val runnable = SimpleRunnable()
    val thread1 = Thread(runnable)
    thread1.start() // Will output: Thread[Thread-1,5,main] has run
}

0

코 루틴과 코 틀린

코 틀린에서는 코 루틴을 사용하여 다음을 수행 할 수 있습니다.

CoroutineScope(Dispatchers.Main).launch { // Main, because UI is changed
    ticker(delayMillis = 1000, initialDelayMillis = 1000).consumeEach {
        tv.append("Hello World")
    }
}

여기서 사용해보십시오 !

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.