Android'de yinelenen görevi planlama


122

Uygulama ön planda olduğu sürece özel bir sunucuya sürekli olarak varlık gönderme görevi olan bir uygulama tasarlıyorum.

Web'deki aramalarımda birkaç farklı yaklaşım gördüm ve bunu yapmanın en iyi yolunun ne olduğunu öğrenmek istedim.

Bir sunucu araması planlamanın en iyi yolu nedir?

Gördüğüm seçenekler şunlardı:

  1. Zamanlayıcı .

  2. ScheduledThreadPoolExecutor .

  3. Hizmet .

  4. AlarmManager ile BroadcastReciever .

Senin düşüncen nedir?

DÜZENLEME:
Buna ihtiyacımın nedeni, tüm kullanıcı eylemlerini uzak bir sunucuya gönderen sohbet tabanlı bir uygulama için.
yani kullanıcı bir mesaj yazıyor, kullanıcı bir mesaj okuyor, kullanıcı çevrimiçidir, kullanıcı çevrim dışıdır vb.

Bu, her aralıkta bir kez sunucuya ne yaptığımı göndermem gerektiği anlamına gelir, çünkü diğer insanlarla bir sohbet odası açtığım için ne yaptığımı bilmeleri gerekir.

Whatsapp mesajı geri bildirim mekanizmasına benzer: mesaj teslim edilmiş gibi görünüyor

DÜZENLEME 2: Android eğitiminin hayati bölümünde okunabileceği gibi, pilin boşalmasını önlemek için
yinelenen görevler artık neredeyse her zaman JobSchedulerAPI (veya FirebaseJobDispatcherdaha düşük API'ler için) aracılığıyla planlanmalıdır.

DÜZENLEME # 3: FirebaseJobDispatcher
kullanımdan kaldırıldı ve JobScheduler'ın özelliklerini de içeren Workmanager ile değiştirildi .


2
AlarmManager ile BroaccastReceiver'ın kullanımı oldukça basittir. Denediğim yukarıdaki alternatiflerden sadece biri.

1
Birden fazla arka plan iş parçacığına izin verdiği ve daha iyi bir çözünürlüğe sahip olduğu (yalnızca ms çözünürlüğü için yararlı) ve istisna işlemeye izin verdiği için daha esnek olan bir ScheduledThreadPoolExecutor üzerinden bir Timer kullanmak için çok az neden vardır. AlarmManager'a gelince, bu gönderi fark hakkında bazı bilgiler verir.
asylias

Kısa çalışma yaşam döngüsü için, yani şu anda ön plandaki bir aktivitede her 30 saniyede bir bazı görevleri gerçekleştirin, ScheduledThreadPoolExecutor (veya Timer) kullanmak daha verimlidir. Uzun çalışma yaşam döngüsü için, yani bir arka plan hizmetinde her 1 saatte bir bazı görevleri gerçekleştirin, AlarmManager kullanımı daha fazla güvenilirlik sağlar.
yorkw

Neden göndermeyi planlamanız gerekiyor? Uygulama açıklamanızdan neden onu gerçek zamanlı olarak göndermiyorsunuz?
iTech

çünkü kullanıcı, zaman aşımı kullanarak çevrimiçi olduğunuzu varsayar. yani, son X süre içinde bir "mevcudiyet" veya "yazarak" mesajı almadıysam, otomatik olarak yapmadığınızı varsayıyorum
thepoosh

Yanıtlar:


164

Emin değilim ama bilgim gereği görüşlerimi paylaşıyorum. Yanlışsam her zaman en iyi cevabı kabul ediyorum.

Alarm Yöneticisi

Alarm Yöneticisi, alarm alıcısının aktif olduğu sürece bir CPU uyanma kilidi tutar. onReceive() yöntemi . Bu, siz yayınla ilgilenene kadar telefonun uyumayacağını garanti eder. Geri onReceive()döndüğünde, Alarm Yöneticisi bu uyandırma kilidini kaldırır. Bu, telefonun bazı durumlarda onReceive()yönteminiz tamamlanır tamamlanmaz uyuyacağı anlamına gelir . Alarm alıcınız aradıysa Context.startService(), istenen servis başlatılmadan önce telefonun uykuya dalması mümkündür. Bunu önlemek için, sizin BroadcastReceiverve Serviceservis kullanılabilir hale gelinceye kadar telefon çalışmaya devam ediyor sağlamak için ayrı bir uyandırma kilit politikasını uygulamak gerekecektir.

Not: Alarm Yöneticisi, uygulamanız o anda çalışmıyor olsa bile, uygulama kodunuzun belirli bir zamanda çalışmasını istediğiniz durumlar için tasarlanmıştır. Normal zamanlama işlemleri için (işaretler, zaman aşımları vb.) İşleyiciyi kullanmak daha kolay ve çok daha etkilidir.

kronometre

timer = new Timer();

    timer.scheduleAtFixedRate(new TimerTask() {

        synchronized public void run() {

            \\ here your todo;
            }

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

Timertarafından çözülen bazı dezavantajları vardır ScheduledThreadPoolExecutor. Bu yüzden en iyi seçim değil

ScheduledThreadPoolExecutor .

Kullanabilirsiniz java.util.TimerScheduledThreadPoolExecutorBir arka plan iş parçacığında düzenli aralıklarla gerçekleşecek bir eylem zamanlamak için veya (tercih edilen) .

İşte ikincisini kullanan bir örnek:

ScheduledExecutorService scheduler =
    Executors.newSingleThreadScheduledExecutor();

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

Bu yüzden tercih ettim ScheduledExecutorService

Ancak, güncellemeler uygulamanız çalışırken gerçekleşecekse, Timerdiğer yanıtlarda önerildiği gibi veya daha yenisini kullanabileceğinizi de düşünün ScheduledThreadPoolExecutor. Uygulamanız çalışmadığında bile güncellenecekse AlarmManager,.

Alarm Manager, uygulamanız o anda çalışmıyor olsa bile, uygulama kodunuzun belirli bir zamanda çalışmasını istediğiniz durumlar için tasarlanmıştır.

Uygulamanız kapalıyken güncelleme yapmayı planlıyorsanız, her on dakikada bir oldukça sık olduğunu ve bu nedenle muhtemelen biraz fazla güç tükettiğini unutmayın.


Bu yöntemi periyodik bir görev için deniyorum, ancak stackoverflow.com/questions/27872016/…
dowjones123

Basit şeyler için - her n saniyede bir durumu kontrol etmek gibi - Zamanlayıcı yapacaktır.
IgorGanapolsky

1
@ Maid786 Bir Hafta aralığında veya Gün cinsinden süre aralığında (Bildirimler göndermek gibi) bir görev yapmak istiyorsak ne kullanmalıyız? Alarm Manager bunun için çok fazla arka plan hesaplaması veya işlemesi yapacak mı?
Chintan Shah

30

kronometre

Javadocs'ta belirtildiği gibi ScheduledThreadPoolExecutor kullanmanız daha iyi olur.

ScheduledThreadPoolExecutor

Kullanım durumunuz birden çok çalışan iş parçacığı gerektirdiğinde ve uyku aralığı kısa olduğunda bu sınıfı kullanın. Ne kadar küçük ? Yaklaşık 15 dakika diyebilirim. AlarmManagerBaşlar şu anda aralıklarını planlamak ve daha küçük uyku aralıkları için bu sınıf kullanılabileceğini önermek gibi görünüyor. Son ifadeyi destekleyecek verilerim yok. Bu bir önsezi.

Hizmet

Hizmetiniz her an VM tarafından kapatılabilir. Yinelenen görevler için hizmetleri kullanmayın. Yinelenen bir görev başlayabilir tamamen başka bir konu olan bir hizmeti .

AlarmManager ile BroadcastReciever

Daha uzun uyku aralıkları için (> 15 dakika), gitmenin yolu budur. AlarmManagerzaten sabitlere sahip (AlarmManager.INTERVAL_DAYbaşlangıçta programlandıktan birkaç gün sonra görevleri tetikleyebileceğini düşündüren ) sahiptir. Kodunuzu çalıştırmak için CPU'yu da uyandırabilir.

Zamanlamanıza ve çalışan iş parçacığı ihtiyaçlarınıza göre bu çözümlerden birini kullanmalısınız.


1
Peki ya uygulamayı kullanmak istersem ve her yarım saatte bir yedekleme yapmak isterdim. Ancak uygulama kullanımda değilken yedekleme yapmak istemiyorum (bu tamamen israf olur). Alarmmanager, yeniden başlatılıncaya kadar eylemi sürekli olarak tekrarlayacaktır (en azından duyduğum şey buydu). Ne önerirsiniz? ScheduledThreadPoolExecutor veya Alarmmanager?
hasdrubal

13

Bunun eski bir soru olduğunu ve yanıtlandığını biliyorum, ancak bu birisine yardımcı olabilir. Senin içindeactivity

private ScheduledExecutorService scheduleTaskExecutor;

İçinde 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

Son teklif Planlama Tekrarlayan Alarmlar - Trade-off anlama docs:

Uygulamanızın kullanım ömrü dışında bir işlemi tetiklemeye yönelik yaygın bir senaryo, verileri bir sunucuyla senkronize etmektir. Bu, tekrarlayan bir alarm kullanmak isteyebileceğiniz bir durumdur. Ancak, uygulamanızın verilerini barındıran sunucuya sahipseniz, senkronizasyon adaptörüyle birlikte Google Cloud Messaging (GCM) kullanmak AlarmManager'dan daha iyi bir çözümdür. Bir senkronizasyon adaptörü size AlarmManager ile aynı programlama seçeneklerini sunar, ancak size önemli ölçüde daha fazla esneklik sunar.

Dolayısıyla, buna göre, bir sunucu çağrısı planlamanın en iyi yolu , senkronizasyon adaptörüyle birlikte Google Cloud Messaging (GCM) kullanmaktır .


1

Kullanıcının tekrar etmek istediği görevin Özel TimeTask run () yöntemini eklediği zamanında görev oluşturdum. başarıyla yineleniyor.

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

}

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.