Ulangi tugas dengan penundaan waktu?

216

Saya memiliki variabel dalam kode saya mengatakan itu adalah "status".

Saya ingin menampilkan beberapa teks dalam aplikasi tergantung pada nilai variabel ini. Ini harus dilakukan dengan penundaan waktu tertentu.

Itu seperti,

  • Periksa nilai variabel status

  • Tampilkan beberapa teks

  • Tunggu 10 detik

  • Periksa nilai variabel status

  • Tampilkan beberapa teks

  • Tunggu 15 detik

dan seterusnya. Waktu tunda dapat bervariasi dan diatur setelah teks ditampilkan.

Saya sudah mencoba Thread.sleep(time delay)dan gagal. Adakah cara yang lebih baik untuk menyelesaikan ini?

Kalpa Pathum Welivitigoda
sumber

Jawaban:

448

Anda harus menggunakan Handler's postDelayedfungsi untuk tujuan ini. Ini akan menjalankan kode Anda dengan penundaan yang ditentukan pada utas UI utama, sehingga Anda dapat memperbarui kontrol 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);
}
inazaruk
sumber
1
Terima kasih inazaruk, berhasil membuatnya bekerja. Ditemukan 2 v kesalahan ketik kecil (di bagian atas "Handler" bukan "Menangani" dan di bagian bawahnya "removeCallbacks" tidak menghapus "removeecallback". Tapi di anycase kodenya persis apa yang saya cari. Berusaha memikirkan apa yang saya bisa lakukan untuk membalas budi. Paling tidak Anda memenangkan rasa hormat saya Salam Aubrey Bourke.
aubreybourke
20
Program yang bagus, bekerja dengan sangat baik. Tetapi startRepeatingTask () harus dipanggil dari metode onCreate / UI thread (butuh beberapa waktu untuk menyadari hal ini!), Mungkin titik ini bisa disebutkan di suatu tempat. Salam
gkris
1
jawaban Anda terus memberi. Ini membantu saya keluar dari lubang hari ini. Terima kasih.
Dean Blakely
Apakah ada cara untuk menjalankan Runnable berulang-ulang di dalam metode getView () dari adaptor?
toobsco42
1
Di sini ketika kita mengimpor kelas apa yang harus kita impor? android.os.Handler atau java.util.logging.Handler?
EJ Chathuranga
34

Bagi siapa pun yang tertarik, inilah kelas yang saya buat menggunakan kode inazaruk yang menciptakan semua yang diperlukan (saya menyebutnya UIUpdater karena saya menggunakannya untuk memperbarui UI secara berkala, tetapi Anda dapat menyebutnya apa pun yang Anda suka):

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

Anda kemudian dapat membuat objek UIUpdater di dalam kelas Anda dan menggunakannya seperti:

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

// Start updates
mUIUpdater.startUpdates();

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

Jika Anda ingin menggunakan ini sebagai pembaru aktivitas, masukkan panggilan awal di dalam metode onResume () dan stop call di dalam onPause (), sehingga pembaruan mulai dan berhenti sesuai dengan visibilitas aktivitas.

ravemir
sumber
1
Diedit: UPDATE_INTERVAL = interval;harus sebelum this(uiUpdater); dalam UIUpdater(Runnable uiUpdater, int interval)(karena nilai UPDATE_INTERVALdigunakan dan harus yang dilewatkan sebagai parameter interval;). Juga hindari lebar lebih dari 80 karakter dalam kode bila memungkinkan (hampir selalu;)
Mr_and_Mrs_D
5
Kelas ini memiliki sejumlah masalah. Di tempat pertama, itu harus dipakai di utas utama untuk dapat memperbarui GUI. Anda bisa dipecahkan ini dengan melewati looper utama untuk konstruktor handler: new Handler(Looper.getMainLooper()). Kedua, itu tidak memvalidasi argumen, sehingga menelan null Runnables dan interval negatif. Akhirnya, itu tidak memperhitungkan waktu yang dihabiskan di uiUpdater.run()garis, atau menangani kemungkinan pengecualian yang dilemparkan oleh metode itu. Juga tidak aman untuk thread, Anda harus membuat startdan stopmenyinkronkan metode.
Tuan Smith
2
Diedit hingga bagian validasi argumen, karena saya tidak punya Eclipse di sini untuk menguji kode. Terima kasih untuk umpan baliknya! Apakah ini yang kamu maksud? Sinkronkan startUpdates dan stopUpdates, serta lakukan panggilan Looper.getMainLooper () di dalam konstruktor Handler (saya harap Anda dapat menyebutnya langsung dari deklarasi lapangan)
ravemir
2
Saya mendapatkan ini: error: call to this must be first statement in constructormungkin ada perbaikan yang mudah.
msysmilu
4
Putus karena memiliki impor - membutuhkan waktu untuk mencari tahu dari mana Handler berasal ketika pemrograman di Jawa dengan santai
Roman Susi
23

Saya pikir hotness baru adalah dengan menggunakan ScheduledThreadPoolExecutor . Seperti itu:

private final ScheduledThreadPoolExecutor executor_ = 
        new ScheduledThreadPoolExecutor(1);
this.executor_.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
    update();
    }
}, 0L, kPeriod, kTimeUnit);
Nels Beckman
sumber
Executors.newSingleThreadScheduledExecutor()bisa menjadi pilihan lain di sini.
Gulshan
13

Timer bekerja dengan baik. Di sini, saya menggunakan Timer untuk mencari teks setelah 1.5s dan memperbarui UI. Semoga itu bisa membantu.

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);
Kai Wang
sumber
di mana Anda menempatkan waktu interval?
Nathiel Barros
1
Hai Nathiel, saya baru saja memperbarui posting saya, semoga membantu! Waktu interval adalah parameter kedua dari Timer.schedule ().
Kai Wang
7

Ada 3 cara untuk melakukannya:

Gunakan ScheduledThreadPoolExecutor

Sedikit berlebihan karena Anda tidak perlu kumpulan Thread

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

Gunakan Pengatur Waktu

Gaya Android Lama

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

Gunakan Handler dan Runnable

Gaya Android Modern

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

Handler Non-Leaky dengan Kegiatan / Konteks

Nyatakan kelas Handler dalam yang tidak membocorkan Memori di kelas Aktivitas / Fragmen Anda

/**
     * 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) {
                // ...
            }
        }
    }

Nyatakan runnable yang akan melakukan tugas berulang Anda di kelas Activity / Fragment Anda

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

         //DO YOUR THINGS
        }
    };

Inisialisasi objek Handler di Activity / Fragment Anda (di sini FlashActivity adalah kelas aktivitas saya)

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

Untuk mengulangi tugas setelah memperbaiki interval waktu

taskHandler.postDelayed (repeatativeTaskRunnable, DELAY_MILLIS);

Untuk menghentikan pengulangan tugas

taskHandler .removeCallbacks (repeatativeTaskRunnable);

PEMBARUAN: Di 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);
    }
Hitesh Sahu
sumber
6

Timer adalah cara lain untuk melakukan pekerjaan Anda, tetapi jangan ragu untuk menambahkan runOnUiThreadjika Anda bekerja dengan UI.

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

 }

}

dan xml adalah ...

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

Cara lain untuk menggunakan CountDownTimer

new CountDownTimer(30000, 1000) {

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

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

Jadwalkan hitung mundur hingga waktu di masa mendatang, dengan pemberitahuan reguler pada interval di sepanjang jalan. Contoh menampilkan hitungan mundur 30 detik dalam bidang teks:

Untuk detail

Zar E Ahmer
sumber
1
Handler lebih disukai daripada Timer. Lihat Timer vs Handler
Suragch
4

Coba contoh berikut ini berfungsi !!!

Gunakan [Handler] dalam metode onCreate () yang memanfaatkan metode postDelayed () yang menyebabkan Runnable ditambahkan ke antrian pesan, untuk dijalankan setelah jumlah waktu yang ditentukan berlalu yaitu 0 pada contoh yang diberikan. 1

Lihat kode ini:

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

Gazal Patel
sumber
4

Berdasarkan posting di atas mengenai ScheduledThreadPoolExecutor , saya datang dengan sebuah utilitas yang sesuai dengan kebutuhan saya (ingin menjalankan metode setiap 3 detik):

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);
    }
}
HelloImKevo
sumber
4

Dalam kasus saya, saya harus menjalankan proses jika salah satu dari kondisi ini benar: jika proses sebelumnya selesai atau jika 5 detik sudah berlalu. Jadi, saya melakukan yang berikut dan bekerja dengan cukup baik:

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


}

Jika process1 dibaca, ia mengeksekusi process2. Jika tidak, itu menambah waktu variabel, dan membuat Handler dieksekusi setelah satu detik. Itu memelihara sebuah loop sampai proses1 dibaca atau kali adalah 5. Ketika kali adalah 5, itu berarti bahwa 5 detik berlalu dan dalam setiap detik, jika klausa process1.isRead () dijalankan.

jvdecamargo
sumber
1

Menggunakan kotlin dan Coroutine-nya cukup mudah, pertama mendeklarasikan pekerjaan di kelas Anda (lebih baik di viewModel Anda) seperti ini:

private var repeatableJob: Job? = null

maka ketika Anda ingin membuat dan memulainya lakukan ini:

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

dan jika Anda ingin menyelesaikannya:

repeatableJob?.cancel()

PS: viewModelScopehanya tersedia dalam model tampilan, Anda dapat menggunakan cakupan Coroutine lain sepertiwithContext(Dispatchers.IO)

Informasi lebih lanjut: Sini

Amin Keshavarzian
sumber
0

Untuk orang yang menggunakan Kotlin, jawaban inazaruk tidak akan berfungsi, IDE akan membutuhkan variabel untuk diinisialisasi, jadi alih-alih menggunakan bagian postDelayeddalam Runnable, kita akan menggunakannya dalam metode yang terpisah.

  • Inisialisasi Runnable seperti ini:

    private var myRunnable = Runnable {
        //Do some work
        //Magic happens here ↓
        runDelayedHandler(1000)   }
  • Inisialisasi runDelayedHandler metode seperti ini:

     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)
        }
    }
  • Seperti yang Anda lihat, pendekatan ini akan membuat Anda dapat mengontrol masa tugas, melacak keepRunningdan mengubahnya selama masa aplikasi akan melakukan pekerjaan untuk Anda.

Tamim Attafi
sumber