Görev gecikmeli olarak tekrarlansın mı?


216

Kodumda "status" olduğunu söyleyen bir değişkenim var.

Bu değişken değerine bağlı olarak uygulamada bazı metin görüntülemek istiyorum. Bu, belirli bir zaman gecikmesi ile yapılmalıdır.

Gibi,

  • Durum değişkeni değerini kontrol et

  • Bazı metinleri göster

  • 10 saniye bekleyin

  • Durum değişkeni değerini kontrol et

  • Bazı metinleri göster

  • 15 saniye bekleyin

ve bunun gibi. Zaman gecikmesi değişebilir ve metin görüntülendiğinde ayarlanır.

Denedim Thread.sleep(time delay)ve başarısız oldu. Bunu yapmanın daha iyi bir yolu var mı?


Yanıtlar:


448

Sen kullanmalıdır Handlerbireyin postDelayedbu amaçla işlevi. Kodunuzu ana UI iş parçacığında belirtilen gecikmeyle çalıştıracak, böylece UI denetimlerini güncelleyebileceksiniz.

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
Teşekkürler inazaruk, işe almayı başardı. 2 v küçük yazım hataları bulundu (üstte "Handler" değil "Handle" ve altta onun "removeCallbacks" "removeecallback" kaldırmak değil. Ama her durumda kod tam olarak ne aradığını oldu. Ne yapabilirim düşünmeye çalışıyorum En azından saygımı kazandın Saygılarımızla Aubrey Bourke
aubreybourke

20
Güzel bir program, kesinlikle iyi çalışıyor. Ancak onCreate yöntemi / UI iş parçacığından startRepeatingTask () çağrılmak zorundaydı (bunu gerçekleştirmek biraz zaman aldı!), Belki de bu nokta bir yerde bahsedilebilirdi. Saygılarımızla
gkris

1
Cevabınız vermeye devam ediyor. Bu bana bugün bir çukurdan yardım etti. Teşekkürler.
Dean Blakely

Bir bağdaştırıcının getView () yöntemi içinde tekrarlanan bir Runnable'a sahip olmanın herhangi bir yolu var mı?
toobsco42

1
Burada sınıfları içe aktarırken neyi içe aktarmalıyız? android.os.Handler veya java.util.logging.Handler?
EJ Chathuranga

34

İlgilenen herkese, inazaruk kodunu kullanarak oluşturduğum bir sınıf gerekli her şeyi yaratıyor (UIUpdater olarak adlandırdım, çünkü UI'yi periyodik olarak güncellemek için kullanıyorum, ancak istediğiniz herhangi bir şeyi arayabilirsiniz):

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

Daha sonra sınıfınızda bir UIUpdater nesnesi oluşturabilir ve bunu aşağıdaki gibi kullanabilirsiniz:

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

// Start updates
mUIUpdater.startUpdates();

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

Bunu etkinlik güncelleyici olarak kullanmak istiyorsanız, başlangıç ​​çağrısını onResume () yönteminin içine ve çağrıyı onPause () içine koyun, böylece güncellemeler etkinlik görünürlüğüne göre başlar ve durur.


1
Düzenlenen: UPDATE_INTERVAL = interval;olmalıdır önce this(uiUpdater); de UIUpdater(Runnable uiUpdater, int interval)(değeri olarak UPDATE_INTERVALkullanılır ve parametre olarak geçirilen bir olmalıdır interval;). Ayrıca mümkünse kodda 80 karakterden fazla genişlikten kaçının (neredeyse her zaman;)
Mr_and_Mrs_D

5
Bu sınıfın bir takım sorunları var. İlk olarak, GUI'yi güncelleyebilmek için ana iş parçacığında örneklendirilmelidir. Sen işleyici yapıcısına ana lüpere geçirerek bu çözebilirdi: new Handler(Looper.getMainLooper()). İkincisi, bağımsız değişkenleri doğrulamaz, bu nedenle boş Çalıştırılabilirleri ve negatif aralıkları yutar. Son olarak, uiUpdater.run()hatta harcanan zamanı dikkate almaz veya bu yöntemle atılan olası istisnaları ele almaz . Ayrıca, iş parçacığı güvenli değil yapmak gerekir startve stopsenkronize yöntemler.
Bay Smith

2
Kod test etmek için burada Tutulma yok gibi, argüman doğrulama bölümüne kadar düzenlendi. Geri dönüşünüz için teşekkür ederiz! Demek istediğin bu mu? Senkronize startUpdates ve stopUpdates ve Handler yapıcısının içine bir Looper.getMainLooper () çağrısı koyun (Umarım doğrudan alan bildiriminden
çağırabilirsiniz

2
Bunu anlıyorum: error: call to this must be first statement in constructorbelki kolay bir düzeltme var.
msysmilu

4
Ithalat için yukarı oy - Java raslantı programlama sırasında Handler nereden geldiğini anlamak için zaman alır
Roman Susi

23

Yeni sıcak bir ScheduledThreadPoolExecutor kullanmak olduğunu düşünüyorum . Şöyle ki:

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

Executors.newSingleThreadScheduledExecutor()burada başka bir seçenek olabilir.
Gulshan

13

Zamanlayıcı iyi çalışıyor. Burada, 1.5 saniye sonra metin aramak ve kullanıcı arayüzünü güncellemek için Zamanlayıcıyı kullanıyorum. Umarım yardımcı olur.

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

aralık süresini nereye koydun?
Nathiel Barros

1
Merhaba Nathiel, az önce yazımı güncelledim, umarım yardımcı olur! Aralık süresi Timer.schedule () öğesinin ikinci parametresidir.
Kai Wang

7

Bunu yapmanın 3 yolu vardır:

ScheduledThreadPoolExecutor kullanın

İş Parçacığı havuzuna ihtiyacınız olmadığından biraz fazlalık

   //----------------------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();
    }

Zamanlayıcı Görevini Kullan

Eski Android Stili

    //----------------------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 ve Runnable kullanın

Modern Android Stili

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

Faaliyet / Bağlam İçeren Sızıntısız İşleyici

Activity / Fragment sınıfınızda Bellek sızdırmayan bir iç Handler sınıfı bildirin

/**
     * 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 sınıfınızda tekrarlayan görevinizi yerine getirecek bir runnable bildirin

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

         //DO YOUR THINGS
        }
    };

Etkinlik / Parçanızdaki İşleyici nesnesini başlat (burada FlashActivity benim etkinlik sınıfımdır)

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

Düzeltme zaman aralığından sonra görevi tekrarlamak için

taskHandler.postDelayed (repeatativeTaskRunnable, DELAY_MILLIS);

Görevin tekrarını durdurmak için

taskHandler .removeCallbacks (repeatativeTaskRunnable);

GÜNCELLEME: Kotlin'de:

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

Zamanlayıcı işinizi yapmanın başka bir yoludur, ancak runOnUiThreadkullanıcı arayüzü ile çalışıyorsanız eklediğinizden emin olun .

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

 }

}

ve 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'i Kullanmanın Başka Bir Yolu

new CountDownTimer(30000, 1000) {

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

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

İlerideki bir zamana kadar geri sayım planlayın ve yol boyunca aralıklarla düzenli olarak bildirim alın. Metin alanında 30 saniyelik geri sayım gösterme örneği:

Detaylar için


1
Zamanlayıcı yerine işleyici tercih edilir. Bkz Handler'a vs Zamanlayıcı
Suragch

4

Aşağıdaki örneği deneyin deneyin !!!

Belirli bir örnekte belirtilen süre geçtikten sonra çalıştırılacak Runnable'ın ileti kuyruğuna eklenmesine neden olan postDelayed () yöntemini kullanan onCreate () yönteminde [Handler] kullanın. 1

Bu kodu inceleyin:

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 ile ilgili yukarıdaki yazıya dayanarak, ihtiyaçlarımı karşılayan bir yardımcı program buldum (her 3 saniyede bir yöntem başlatmak istedim):

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

Benim durumumda, bu koşullardan biri doğruysa bir işlem yürütmeliydim: önceki bir işlem tamamlanmışsa veya 5 saniye geçmişse. Bu yüzden aşağıdakileri yaptım ve oldukça iyi çalıştım:

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


}

Süreç1 okunursa, süreç2'yi yürütür. Değilse, değişken süreleri artırır ve İşleyiciyi bir saniye sonra yürütür. Process1 okunana veya süreler 5 olana kadar bir döngü sürdürür. Times 5 olduğunda, 5 saniyenin geçtiği ve her saniyede process1.isRead () öğesinin if deyiminin yürütüldüğü anlamına gelir.


1

Kotlin ve Coroutine'i kullanmak oldukça kolaydır, önce sınıfınızda bir iş ilan edin (viewModel'de daha iyi):

private var repeatableJob: Job? = null

daha sonra oluşturmak ve başlatmak istediğinizde bunu yapın:

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

ve bitirmek istiyorsanız:

repeatableJob?.cancel()

Not: viewModelScopeyalnızca görünüm modellerinde kullanılabilir, örneğin diğer Coroutine kapsamlarını kullanabilirsinizwithContext(Dispatchers.IO)

Daha fazla bilgi: Burada


0

KOTLIN kullanan kişiler için inazaruk cevabı böylece kullanmak yerine, değişken gerektirecektir IDE başlatılması için, işe yaramaz postDelayediçeride Runnable, biz ayrı yöntemde kullanacağız.

  • RunnableBunu şu şekilde başlatın :

    private var myRunnable = Runnable {
        //Do some work
        //Magic happens here ↓
        runDelayedHandler(1000)   }
  • runDelayedHandlerYönteminizi şu şekilde başlatın :

     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)
        }
    }
  • Gördüğünüz gibi, bu yaklaşım görevin kullanım ömrünü kontrol edebilmenizi sağlayacak keepRunning, uygulamanın ömrü boyunca bunu takip edebilecek ve değiştirebileceksiniz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.