시간 지연으로 작업을 반복 하시겠습니까?


216

코드에 변수가 "상태"라고 표시되어 있습니다.

이 변수 값에 따라 응용 프로그램에 텍스트를 표시하고 싶습니다. 이 작업은 특정 시간 지연으로 수행해야합니다.

마치

  • 상태 변수 값 확인

  • 일부 텍스트 표시

  • 10 초 동안 기다리십시오

  • 상태 변수 값 확인

  • 일부 텍스트 표시

  • 15 초 동안 기다립니다

등등. 시간 지연은 다를 수 있으며 텍스트가 표시되면 설정됩니다.

시도했지만 Thread.sleep(time delay)실패했습니다. 이 작업을 수행하는 더 좋은 방법이 있습니까?


답변:


448

이 목적으로 HandlerpostDelayed기능을 사용해야합니다 . 기본 UI 스레드에서 지정된 지연으로 코드를 실행하므로 UI ​​컨트롤을 업데이트 할 수 있습니다.

private int mInterval = 5000; // 5 seconds by default, can be changed later
private Handler mHandler;

@Override
protected void onCreate(Bundle bundle) {

    // your code here

    mHandler = new Handler();
    startRepeatingTask();
}

@Override
public void onDestroy() {
    super.onDestroy();
    stopRepeatingTask();
}

Runnable mStatusChecker = new Runnable() {
    @Override 
    public void run() {
          try {
               updateStatus(); //this function can change value of mInterval.
          } finally {
               // 100% guarantee that this always happens, even if
               // your update method throws an exception
               mHandler.postDelayed(mStatusChecker, mInterval);
          }
    }
};

void startRepeatingTask() {
    mStatusChecker.run(); 
}

void stopRepeatingTask() {
    mHandler.removeCallbacks(mStatusChecker);
}

1
inazaruk에게 감사드립니다. 2 개의 작은 오타가 발견되었습니다 ( "핸들"이 아닌 "핸들러"의 맨 위와 "removeCallbacks"의 맨 아래에있는 "removeCallbacks"는 "removecallback"을 제거하지 않습니다.) 가장 존경하는 사람은 최소한 내 존경을
얻은 것

20
좋은 프로그램, 절대적으로 잘 작동합니다. 그러나 startRepeatingTask ()는 onCreate 메소드 / UI 스레드에서 호출해야했습니다 (이를 깨닫는 데 시간이 걸렸습니다!), 아마도이 시점은 어딘가에 언급되었을 수 있습니다. 안부
gkris

1
당신의 대답은 계속 제공합니다. 이것은 오늘 구멍에서 나에게 도움이되었습니다. 감사.
Dean Blakely

어댑터의 getView () 메소드 내에 Runnable을 반복적으로 사용하는 방법이 있습니까?
toobsco42

1
여기서 클래스를 가져올 때 무엇을 가져와야합니까? android.os.Handler 또는 java.util.logging.Handler?
EJ Chathuranga

34

관심있는 사람에게, 여기에 필요한 모든 것을 만드는 inazaruk의 코드를 사용하여 만든 클래스가 있습니다 (UI를 주기적으로 업데이트하기 위해 UIUpdater라고 불렀지 만 원하는대로 호출 할 수 있습니다).

import android.os.Handler;
/**
 * A class used to perform periodical updates,
 * specified inside a runnable object. An update interval
 * may be specified (otherwise, the class will perform the 
 * update every 2 seconds).
 * 
 * @author Carlos Simões
 */
public class UIUpdater {
        // Create a Handler that uses the Main Looper to run in
        private Handler mHandler = new Handler(Looper.getMainLooper());

        private Runnable mStatusChecker;
        private int UPDATE_INTERVAL = 2000;

        /**
         * Creates an UIUpdater object, that can be used to
         * perform UIUpdates on a specified time interval.
         * 
         * @param uiUpdater A runnable containing the update routine.
         */
        public UIUpdater(final Runnable uiUpdater) {
            mStatusChecker = new Runnable() {
                @Override
                public void run() {
                    // Run the passed runnable
                    uiUpdater.run();
                    // Re-run it after the update interval
                    mHandler.postDelayed(this, UPDATE_INTERVAL);
                }
            };
        }

        /**
         * The same as the default constructor, but specifying the
         * intended update interval.
         * 
         * @param uiUpdater A runnable containing the update routine.
         * @param interval  The interval over which the routine
         *                  should run (milliseconds).
         */
        public UIUpdater(Runnable uiUpdater, int interval){
            UPDATE_INTERVAL = interval;
            this(uiUpdater);
        }

        /**
         * Starts the periodical update routine (mStatusChecker 
         * adds the callback to the handler).
         */
        public synchronized void startUpdates(){
            mStatusChecker.run();
        }

        /**
         * Stops the periodical update routine from running,
         * by removing the callback.
         */
        public synchronized void stopUpdates(){
            mHandler.removeCallbacks(mStatusChecker);
        }
}

그런 다음 클래스 내에 UIUpdater 객체를 만들어 다음과 같이 사용할 수 있습니다.

...
mUIUpdater = new UIUpdater(new Runnable() {
         @Override 
         public void run() {
            // do stuff ...
         }
    });

// Start updates
mUIUpdater.startUpdates();

// Stop updates
mUIUpdater.stopUpdates();
...

이를 활동 업데이터로 사용하려면 onResume () 메소드 안에 시작 호출을, onPause () 안에 중지 호출을 넣으면 활동 가시성에 따라 업데이트가 시작 및 중지됩니다.


1
편집은 : UPDATE_INTERVAL = interval;이어야 이전 this(uiUpdater);UIUpdater(Runnable uiUpdater, int interval)(의 값으로 UPDATE_INTERVAL사용되며, 상기 파라미터로 전달 된 것이어야한다 interval;). 가능하면 코드에서 80 자 이상을 피하십시오 (거의 항상;)
Mr_and_Mrs_D

5
이 수업에는 많은 문제가 있습니다. 우선 GUI를 업데이트 할 수 있도록 메인 스레드에서 인스턴스화해야합니다. 메인 루퍼를 핸들러 생성자에 전달 하여이 문제를 해결할 수 있습니다 new Handler(Looper.getMainLooper()). 둘째, 인수의 유효성을 검사하지 않으므로 null Runnables 및 음수 간격을 삼 킵니다. 마지막으로 uiUpdater.run()라인 에서 소비 한 시간을 고려하지 않으며 해당 메소드에서 발생하는 가능한 예외를 처리하지 않습니다. 또한 그것은, 스레드로부터 안전하지 않습니다 당신은해야 start하고 stop동기화 방법.
Mister Smith

2
코드를 테스트 할 Eclipse가 없기 때문에 인수 유효성 검사 부분까지 편집했습니다. 피드백 감사드립니다! 이것이 당신이 의미 한 것입니까? 동기화 된 startUpdates 및 stopUpdates를 처리기 생성자 내부에 Looper.getMainLooper () 호출을 넣습니다 (필드 선언에서 직접 호출 할 수 있기를 바랍니다)
ravemir

2
나는 이것을 얻는다 : error: call to this must be first statement in constructor쉬운 수정이있을 수 있습니다.
msysmilu

4
수입있는을위한 Upvoting - 부담없이 자바로 프로그래밍 할 때부터 핸들러가 오는 곳 알아 내기 위해 시간이 걸립니다
로마 수시

23

새로운 핫 니스는 ScheduledThreadPoolExecutor 를 사용하는 것 입니다. 이렇게 :

private final ScheduledThreadPoolExecutor executor_ = 
        new ScheduledThreadPoolExecutor(1);
this.executor_.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
    update();
    }
}, 0L, kPeriod, kTimeUnit);

Executors.newSingleThreadScheduledExecutor()또 다른 옵션이 될 수 있습니다.
Gulshan

13

타이머가 제대로 작동합니다. 여기서는 타이머를 사용하여 1.5 초 후에 텍스트를 검색하고 UI를 업데이트합니다. 희망이 도움이됩니다.

private Timer _timer = new Timer();

_timer.schedule(new TimerTask() {
    @Override
    public void run() {
        // use runOnUiThread(Runnable action)
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                search();
            }
        });
    }
}, timeInterval);

간격 시간을 어디에 두었습니까?
Nathiel Barros

1
안녕하세요 Nathiel, 방금 게시물을 업데이트했습니다. 도움이 되길 바랍니다. 간격 시간은 Timer.schedule ()의 두 번째 매개 변수입니다.
Kai Wang

7

3 가지 방법이 있습니다 :

ScheduledThreadPoolExecutor 사용

스레드 풀이 필요하지 않기 때문에 약간의 과잉

   //----------------------SCHEDULER-------------------------
    private final ScheduledThreadPoolExecutor executor_ =
            new ScheduledThreadPoolExecutor(1);
     ScheduledFuture<?> schedulerFuture;
   public void  startScheduler() {
       schedulerFuture=  executor_.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                //DO YOUR THINGS
                pageIndexSwitcher.setVisibility(View.GONE);
            }
        }, 0L, 5*MILLI_SEC,  TimeUnit.MILLISECONDS);
    }


    public void  stopScheduler() {
        pageIndexSwitcher.setVisibility(View.VISIBLE);
        schedulerFuture.cancel(false);
        startScheduler();
    }

타이머 작업 사용

오래된 안드로이드 스타일

    //----------------------TIMER  TASK-------------------------

    private Timer carousalTimer;
    private void startTimer() {
        carousalTimer = new Timer(); // At this line a new Thread will be created
        carousalTimer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                //DO YOUR THINGS
                pageIndexSwitcher.setVisibility(INVISIBLE);
            }
        }, 0, 5 * MILLI_SEC); // delay
    }

    void stopTimer() {
        carousalTimer.cancel();
    }

처리기 및 실행 가능 사용

현대 안드로이드 스타일

    //----------------------HANDLER-------------------------

    private Handler taskHandler = new android.os.Handler();

    private Runnable repeatativeTaskRunnable = new Runnable() {
        public void run() {
            //DO YOUR THINGS
        }
    };

   void startHandler() {
        taskHandler.postDelayed(repeatativeTaskRunnable, 5 * MILLI_SEC);
    }

    void stopHandler() {
        taskHandler.removeCallbacks(repeatativeTaskRunnable);
    }

활동 / 컨텍스트가있는 비누 설 처리기

Activity / Fragment 클래스에서 메모리누출되지 않는 내부 핸들러 클래스를 선언 하십시오.

/**
     * Instances of static inner classes do not hold an implicit
     * reference to their outer class.
     */
    private static class NonLeakyHandler extends Handler {
        private final WeakReference<FlashActivity> mActivity;

        public NonLeakyHandler(FlashActivity activity) {
            mActivity = new WeakReference<FlashActivity>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            FlashActivity activity = mActivity.get();
            if (activity != null) {
                // ...
            }
        }
    }

Activity / Fragment 클래스에서 반복적 인 작업을 수행 할 실행 파일을 선언하십시오.

   private Runnable repeatativeTaskRunnable = new Runnable() {
        public void run() {
            new Handler(getMainLooper()).post(new Runnable() {
                @Override
                public void run() {

         //DO YOUR THINGS
        }
    };

Activity / Fragment에서 Handler 객체 초기화 (여기서는 FlashActivity가 내 활동 클래스 임)

//Task Handler
private Handler taskHandler = new NonLeakyHandler(FlashActivity.this);

수정 시간 간격 후에 작업을 반복하려면

taskHandler.postDelayed (반복 작업 실행, DELAY_MILLIS);

작업 반복을 중지하려면

taskHandler .removeCallbacks (repeatativeTaskRunnable);

업데이트 : Kotlin에서 :

    //update interval for widget
    override val UPDATE_INTERVAL = 1000L

    //Handler to repeat update
    private val updateWidgetHandler = Handler()

    //runnable to update widget
    private var updateWidgetRunnable: Runnable = Runnable {
        run {
            //Update UI
            updateWidget()
            // Re-run it after the update interval
            updateWidgetHandler.postDelayed(updateWidgetRunnable, UPDATE_INTERVAL)
        }

    }

 // SATART updating in foreground
 override fun onResume() {
        super.onResume()
        updateWidgetHandler.postDelayed(updateWidgetRunnable, UPDATE_INTERVAL)
    }


    // REMOVE callback if app in background
    override fun onPause() {
        super.onPause()
        updateWidgetHandler.removeCallbacks(updateWidgetRunnable);
    }

6

타이머는 작업을 수행하는 또 다른 방법이지만 runOnUiThreadUI로 작업하는 경우 조용히 추가하십시오 .

    import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Timer;
import java.util.TimerTask;

import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.TextView;
import android.app.Activity;

public class MainActivity extends Activity {

 CheckBox optSingleShot;
 Button btnStart, btnCancel;
 TextView textCounter;

 Timer timer;
 MyTimerTask myTimerTask;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  optSingleShot = (CheckBox)findViewById(R.id.singleshot);
  btnStart = (Button)findViewById(R.id.start);
  btnCancel = (Button)findViewById(R.id.cancel);
  textCounter = (TextView)findViewById(R.id.counter);

  btnStart.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View arg0) {

    if(timer != null){
     timer.cancel();
    }

    //re-schedule timer here
    //otherwise, IllegalStateException of
    //"TimerTask is scheduled already" 
    //will be thrown
    timer = new Timer();
    myTimerTask = new MyTimerTask();

    if(optSingleShot.isChecked()){
     //singleshot delay 1000 ms
     timer.schedule(myTimerTask, 1000);
    }else{
     //delay 1000ms, repeat in 5000ms
     timer.schedule(myTimerTask, 1000, 5000);
    }
   }});

  btnCancel.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View v) {
    if (timer!=null){
     timer.cancel();
     timer = null;
    }
   }
  });

 }

 class MyTimerTask extends TimerTask {

  @Override
  public void run() {
   Calendar calendar = Calendar.getInstance();
   SimpleDateFormat simpleDateFormat = 
     new SimpleDateFormat("dd:MMMM:yyyy HH:mm:ss a");
   final String strDate = simpleDateFormat.format(calendar.getTime());

   runOnUiThread(new Runnable(){

    @Override
    public void run() {
     textCounter.setText(strDate);
    }});
  }

 }

}

그리고 XML은 ...

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:orientation="vertical"
tools:context=".MainActivity" >

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"
    android:autoLink="web"
    android:text="http://android-er.blogspot.com/"
    android:textStyle="bold" />
<CheckBox 
    android:id="@+id/singleshot"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Single Shot"/>

CountDownTimer를 사용하는 다른 방법

new CountDownTimer(30000, 1000) {

     public void onTick(long millisUntilFinished) {
         mTextField.setText("seconds remaining: " + millisUntilFinished / 1000);
     }

     public void onFinish() {
         mTextField.setText("done!");
     }
  }.start();

진행되는 간격에 대한 정기적 인 알림과 함께 향후 시간까지 카운트 다운을 예약하십시오. 텍스트 필드에 30 초 카운트 다운을 표시하는 예 :

자세한 내용


1
핸들러는 타이머보다 선호됩니다.
Suragch

4

다음 예제를 시도해보십시오 !!!

주어진 예제에서 지정된 시간이 경과 한 후 Runnable이 메시지 큐에 추가되도록하는 postDelayed () 메소드를 사용하는 onCreate () 메소드에서 [Handler]를 사용하십시오. 1

이 코드를 참조하십시오 :

public void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
       setContentView(R.layout.main);
    //------------------
    //------------------
    android.os.Handler customHandler = new android.os.Handler();
            customHandler.postDelayed(updateTimerThread, 0);
}

private Runnable updateTimerThread = new Runnable()
{
        public void run()
        {
            //write here whaterver you want to repeat
            customHandler.postDelayed(this, 1000);
        }
};



4

ScheduledThreadPoolExecutor에 관한 위의 게시물을 기반으로 , 나는 내 필요에 맞는 유틸리티를 생각해 냈습니다 (3 초마다 메소드를 실행하고 싶었습니다).

class MyActivity {
    private ScheduledThreadPoolExecutor mDialogDaemon;

    private void initDebugButtons() {
        Button btnSpawnDialogs = (Button)findViewById(R.id.btn_spawn_dialogs);
        btnSpawnDialogs.setVisibility(View.VISIBLE);
        btnSpawnDialogs.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                spawnDialogs();
            }
        });
    }

    private void spawnDialogs() {
        if (mDialogDaemon != null) {
            mDialogDaemon.shutdown();
            mDialogDaemon = null;
        }
        mDialogDaemon = new ScheduledThreadPoolExecutor(1);
        // This process will execute immediately, then execute every 3 seconds.
        mDialogDaemon.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        // Do something worthwhile
                    }
                });
            }
        }, 0L, 3000L, TimeUnit.MILLISECONDS);
    }
}

4

제 경우에는 다음 조건 중 하나에 해당하는 경우 프로세스를 실행해야했습니다. 이전 프로세스가 완료되었거나 5 초가 이미 지났을 경우. 그래서 나는 다음을했고 꽤 잘했습니다.

private Runnable mStatusChecker;
private Handler mHandler;

class {
method() {
  mStatusChecker = new Runnable() {
            int times = 0;
            @Override
            public void run() {
                if (times < 5) {
                    if (process1.isRead()) {
                        executeProcess2();
                    } else {
                        times++;
                        mHandler.postDelayed(mStatusChecker, 1000);
                    }
                } else {
                    executeProcess2();
                }
            }
        };

        mHandler = new Handler();
        startRepeatingTask();
}

    void startRepeatingTask() {
       mStatusChecker.run();
    }

    void stopRepeatingTask() {
        mHandler.removeCallbacks(mStatusChecker);
    }


}

process1을 읽으면 process2를 실행합니다. 그렇지 않으면 변수 시간이 증가하고 1 초 후에 핸들러가 실행됩니다. process1을 읽거나 시간이 5가 될 때까지 루프를 유지합니다. 시간이 5 일 때 5 초가 지나고 매초마다 process1.isRead ()의 if 절이 실행됨을 의미합니다.


1

kotlin과 그 코 루틴을 사용하면 매우 쉽습니다. 먼저 클래스에서 작업을 선언하십시오 (viewModel에서 더 낫습니다).

private var repeatableJob: Job? = null

그런 다음 작성하고 시작하려면 다음을 수행하십시오.

repeatableJob = viewModelScope.launch {
    while (isActive) {
         delay(5_000)
         loadAlbums(iImageAPI, titleHeader, true)
    }
}
repeatableJob?.start()

그리고 당신이 그것을 끝내고 싶다면 :

repeatableJob?.cancel()

PS : viewModelScope뷰 모델에서만 사용할 수 있으며 다음과 같은 다른 코 루틴 범위를 사용할 수 있습니다withContext(Dispatchers.IO)

자세한 정보 : 여기


0

Kotlin을 사용하는 사람들 에게는 inazaruk의 대답 이 효과가 없으며 IDE는 변수를 초기화해야하므로 postDelayed내부 를 사용하는 대신 Runnable별도의 방법으로 사용합니다.

  • 다음 Runnable과 같이 초기화하십시오 .

    private var myRunnable = Runnable {
        //Do some work
        //Magic happens here ↓
        runDelayedHandler(1000)   }
  • 다음 runDelayedHandler과 같이 메소드를 초기화하십시오 .

     private fun runDelayedHandler(timeToWait : Long) {
        if (!keepRunning) {
            //Stop your handler
            handler.removeCallbacksAndMessages(null)
            //Do something here, this acts like onHandlerStop
        }
        else {
            //Keep it running
            handler.postDelayed(myRunnable, timeToWait)
        }
    }
  • 보시다시피이 접근 방식을 사용하면 작업 수명을 제어 keepRunning하고 응용 프로그램 수명 동안 추적 하고 변경하여 작업을 수행 할 수 있습니다.

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