Menjadwalkan tugas berulang di Android


122

Saya merancang aplikasi yang memiliki tugas berulang untuk mengirimkan kehadiran ke server khusus selama aplikasi berada di latar depan.

Dalam penelusuran saya di seluruh web, saya melihat beberapa pendekatan berbeda dan ingin tahu cara terbaik untuk melakukan ini.

Apa cara terbaik untuk menjadwalkan panggilan server?

Opsi yang saya lihat adalah:

  1. Timer .

  2. ScheduledThreadPoolExecutor .

  3. Layanan .

  4. BroadcastReciever dengan AlarmManager .

Apa pendapatmu?

EDIT:
Alasan saya memerlukan ini adalah untuk aplikasi berbasis obrolan yang mengirimkan semua tindakan pengguna ke server jarak jauh.
yaitu pengguna mengetik pesan, pengguna membaca pesan, pengguna online, pengguna offline dll.

Ini berarti bahwa sekali setiap interval, saya perlu mengirim server apa yang saya lakukan, karena saya membuka ruang obrolan dengan orang lain, mereka perlu tahu apa yang saya lakukan.

Mirip dengan mekanisme umpan balik pesan whatsapp: pesan terlihat terkirim

EDIT # 2:
Tugas berulang sekarang seharusnya dijadwalkan hampir selalu melalui JobSchedulerAPI (atau FirebaseJobDispatcheruntuk API yang lebih rendah) untuk mencegah masalah pengurasan baterai seperti yang dapat dibaca di bagian vital dari pelatihan Android

EDIT # 3:
FirebaseJobDispatcher sudah tidak digunakan lagi dan diganti dengan Workmanager , yang juga menggabungkan fitur JobScheduler.


2
BroaccastReceiver dengan AlarmManager cukup mudah digunakan. Ini satu-satunya alternatif di atas yang pernah saya coba.

1
Ada sedikit alasan untuk menggunakan Timer melalui ScheduledThreadPoolExecutor, yang lebih fleksibel karena memungkinkan lebih dari satu utas latar belakang dan memiliki resolusi yang lebih baik (hanya berguna untuk resolusi ms) dan memungkinkan penanganan pengecualian. Adapun AlarmManager, posting ini memberikan beberapa informasi tentang perbedaannya.
assylias

Untuk siklus hidup yang berjalan singkat, yaitu melakukan beberapa tugas setiap 30 detik dalam aktivitas yang saat ini berada di latar depan, gunakan ScheduledThreadPoolExecutor (atau Timer) yang lebih efisien. Untuk siklus hidup yang berjalan lama, yaitu melakukan beberapa tugas setiap 1 jam di layanan latar belakang, penggunaan AlarmManager memberikan keandalan lebih.
yorkw

Mengapa Anda bahkan perlu menjadwalkan pengiriman? Dari deskripsi aplikasi Anda mengapa tidak Anda kirimkan saja secara real-time?
iTech

karena pengguna menganggap Anda sedang online, menggunakan waktu tunggu. artinya, jika saya belum menerima pesan "kehadiran" atau "sedang mengetik" dalam waktu X yang lalu, saya secara otomatis menganggap Anda tidak melakukannya
thepoosh

Jawaban:


164

Saya tidak yakin tetapi menurut pengetahuan saya, saya berbagi pandangan saya. Saya selalu menerima jawaban terbaik jika saya salah.

Manajer Alarm

Manajer Alarm menahan penguncian bangun CPU selama onReceive()metode penerima alarm sedang dijalankan. Ini menjamin bahwa telepon tidak akan tidur sampai Anda selesai menangani siaran. Setelah onReceive()kembali, Manajer Alarm melepaskan kunci bangun ini. Artinya, dalam beberapa kasus ponsel akan tidur segera setelah onReceive()metode Anda selesai. Jika penerima alarm Anda menelepon Context.startService(), ada kemungkinan ponsel akan tidur sebelum layanan yang diminta diluncurkan. Untuk mencegah hal ini, Anda BroadcastReceiverdan Serviceperlu menerapkan kebijakan penguncian layar saat aktif terpisah untuk memastikan bahwa ponsel terus berjalan hingga layanan tersedia.

Catatan: Manajer Alarm ditujukan untuk kasus di mana Anda ingin kode aplikasi Anda dijalankan pada waktu tertentu, bahkan jika aplikasi Anda saat ini tidak berjalan. Untuk operasi pengaturan waktu normal (tick, timeout, dll) lebih mudah dan lebih efisien untuk menggunakan Handler.

Timer

timer = new Timer();

    timer.scheduleAtFixedRate(new TimerTask() {

        synchronized public void run() {

            \\ here your todo;
            }

        }}, TimeUnit.MINUTES.toMillis(1), TimeUnit.MINUTES.toMillis(1));

Timermemiliki beberapa kekurangan yang dapat diatasi ScheduledThreadPoolExecutor. Jadi itu bukan pilihan terbaik

ScheduledThreadPoolExecutor .

Anda dapat menggunakan java.util.Timeratau ScheduledThreadPoolExecutor(lebih disukai) untuk menjadwalkan tindakan agar terjadi secara berkala di thread latar belakang.

Berikut ini contoh yang menggunakan yang terakhir:

ScheduledExecutorService scheduler =
    Executors.newSingleThreadScheduledExecutor();

scheduler.scheduleAtFixedRate
      (new Runnable() {
         public void run() {
            // call service
         }
      }, 0, 10, TimeUnit.MINUTES);

Jadi saya lebih suka ScheduledExecutorService

Tetapi juga pikirkan tentang bahwa jika pembaruan akan terjadi saat aplikasi Anda berjalan, Anda dapat menggunakan Timer, seperti yang disarankan dalam jawaban lain, atau yang lebih baru ScheduledThreadPoolExecutor. Jika aplikasi Anda akan diperbarui bahkan saat tidak berjalan, Anda harus menggunakan AlarmManager.

Manajer Alarm ditujukan untuk kasus di mana Anda ingin kode aplikasi Anda dijalankan pada waktu tertentu, bahkan jika aplikasi Anda saat ini tidak berjalan.

Perhatikan bahwa jika Anda berencana memperbarui saat aplikasi Anda dimatikan, cukup sering setiap sepuluh menit, dan dengan demikian mungkin sedikit terlalu memakan daya.


Saya mencoba metode ini untuk tugas berkala, tetapi tampaknya tidak berfungsi stackoverflow.com/questions/27872016/…
dowjones123

Untuk hal-hal sederhana - seperti memeriksa status setiap n-detik -Timer sudah cukup.
IgorGanapolsky

1
@ Maid786 Apa yang harus kita gunakan jika kita ingin melakukan beberapa tugas (seperti mengirim Pemberitahuan) pada interval Minggu atau durasi dalam Hari? Apakah Alarm Manager akan mengambil terlalu banyak penghitungan atau pemrosesan latar belakang untuk itu?
Chintan Shah

30

Timer

Seperti yang disebutkan di javadocs Anda lebih baik menggunakan ScheduledThreadPoolExecutor.

ScheduledThreadPoolExecutor

Gunakan kelas ini jika kasus penggunaan Anda memerlukan beberapa utas pekerja dan interval tidurnya kecil. Seberapa kecil? Yah, menurutku sekitar 15 menit. The AlarmManagerdimulai menjadwalkan interval saat ini dan tampaknya menunjukkan bahwa untuk interval tidur yang lebih kecil kelas ini dapat digunakan. Saya tidak punya data untuk mendukung pernyataan terakhir. Itu adalah firasat.

Layanan

Layanan Anda dapat ditutup kapan saja oleh VM. Jangan gunakan layanan untuk tugas berulang. Tugas berulang dapat memulai layanan, yang merupakan masalah lain sepenuhnya.

BroadcastReciever dengan AlarmManager

Untuk interval tidur yang lebih lama (> 15 menit), inilah caranya. AlarmManagersudah memiliki konstanta ( AlarmManager.INTERVAL_DAY) yang menunjukkan bahwa ini bisa memicu tugas beberapa hari setelah awalnya dijadwalkan. Itu juga dapat membangunkan CPU untuk menjalankan kode Anda.

Anda harus menggunakan salah satu solusi tersebut berdasarkan waktu dan kebutuhan thread pekerja Anda.


1
Jadi bagaimana jika saya ingin menggunakan aplikasi, dan setiap setengah jam saya ingin membuat cadangan. Tapi saya tidak ingin membuat cadangan saat aplikasi tidak digunakan (itu akan sia-sia). Alarmmanager akan terus mengulangi tindakan tersebut hingga reboot (setidaknya itulah yang saya dengar). Apa yang akan Anda rekomendasikan? ScheduledThreadPoolExecutor atau Alarmmanager?
hasdrubal

13

Saya menyadari ini adalah pertanyaan lama dan telah dijawab tetapi ini dapat membantu seseorang. Di Andaactivity

private ScheduledExecutorService scheduleTaskExecutor;

Di onCreate

  scheduleTaskExecutor = Executors.newScheduledThreadPool(5);

    //Schedule a task to run every 5 seconds (or however long you want)
    scheduleTaskExecutor.scheduleAtFixedRate(new Runnable() {
        @Override
        public void run() {
            // Do stuff here!

            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    // Do stuff to update UI here!
                    Toast.makeText(MainActivity.this, "Its been 5 seconds", Toast.LENGTH_SHORT).show();
                }
            });

        }
    }, 0, 5, TimeUnit.SECONDS); // or .MINUTES, .HOURS etc.

2

Mengutip Alarm Berulang Penjadwalan - Pahami kompromi dokumen:

Skenario umum untuk memicu operasi di luar masa pakai aplikasi Anda adalah menyinkronkan data dengan server. Ini adalah kasus di mana Anda mungkin tergoda untuk menggunakan alarm berulang. Namun jika Anda memiliki server yang menghosting data aplikasi Anda, menggunakan Google Cloud Messaging (GCM) bersama dengan adaptor sinkronisasi adalah solusi yang lebih baik daripada AlarmManager. Adaptor sinkronisasi memberi Anda semua opsi penjadwalan yang sama dengan AlarmManager, tetapi menawarkan fleksibilitas yang jauh lebih besar.

Jadi, berdasarkan ini, cara terbaik untuk menjadwalkan panggilan server adalah menggunakan Google Cloud Messaging (GCM) bersama dengan adaptor sinkronisasi .


1

Saya telah membuat tugas tepat waktu di mana tugas yang ingin diulang pengguna, tambahkan metode run () Tugas Waktu Kustom. itu berhasil berulang.

 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;
 import android.content.Intent;

 public class MainActivity extends Activity {

     CheckBox optSingleShot;
     Button btnStart, btnCancel;
     TextView textCounter;

     Timer timer;
     MyTimerTask myTimerTask;

     int tobeShown = 0  ;

@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);
    tobeShown = 1;

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

    btnStart.setOnClickListener(new OnClickListener(){

        @Override
        public void onClick(View arg0) {


            Intent i = new Intent(MainActivity.this, ActivityB.class);
            startActivity(i);

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

    btnCancel.setOnClickListener(new OnClickListener(){

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

}

@Override
protected void onResume() {
    super.onResume();

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


@Override
protected void onPause() {
    super.onPause();

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

}

@Override
protected void onStop() {
    super.onStop();

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

}

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.