Harmonogram zadań dla systemu Android

W tym przewodniku omówiono sposób planowania pracy w tle przy użyciu interfejsu API harmonogramu zadań systemu Android, który jest dostępny na urządzeniach z systemem Android 5.0 (poziom 21 interfejsu API) i nowszym.

Omówienie

Jednym z najlepszych sposobów reagowania aplikacji systemu Android na użytkownika jest zapewnienie, że złożone lub długotrwałe prace są wykonywane w tle. Jednak ważne jest, aby praca w tle nie wpływała negatywnie na środowisko użytkownika z urządzeniem.

Na przykład zadanie w tle może sondować witrynę internetową co trzy lub cztery minuty, aby wykonać zapytanie o zmiany w określonym zestawie danych. Wydaje się to łagodne, jednak miałoby to katastrofalny wpływ na żywotność baterii. Aplikacja będzie wielokrotnie wznawiać urządzenie, podnieść poziom procesora CPU do wyższego stanu zasilania, włączyć urządzenia radiowe, wysyłać żądania sieciowe, a następnie przetwarzać wyniki. Pogarsza się, ponieważ urządzenie nie będzie natychmiast zasilać i wracać do stanu bezczynności o niskiej mocy. Nieprawidłowo zaplanowane prace w tle mogą przypadkowo zachować urządzenie w stanie z niepotrzebnymi i nadmiernymi wymaganiami dotyczącymi zasilania. Ta pozornie niewinna aktywność (sondowanie witryny internetowej) sprawi, że urządzenie będzie bezużyteczne w stosunkowo krótkim czasie.

System Android udostępnia następujące interfejsy API, które ułatwiają wykonywanie pracy w tle, ale same w sobie nie są wystarczające do inteligentnego planowania zadań.

  • Intent Services — usługi Intent Services doskonale nadają się do wykonywania pracy, ale nie zapewniają możliwości planowania pracy.
  • AlarmManager — te interfejsy API zezwalają tylko na zaplanowanie pracy, ale nie zapewniają możliwości rzeczywistego wykonania pracy. Ponadto menedżer alarmów zezwala tylko na ograniczenia oparte na czasie, co oznacza zgłaszanie alarmu w określonym czasie lub po upływie określonego czasu.
  • Odbiorniki rozgłaszań — aplikacja systemu Android może skonfigurować odbiorniki emisji w celu wykonywania pracy w odpowiedzi na zdarzenia lub intencje dla całego systemu. Jednak odbiorniki emisji nie zapewniają żadnej kontroli nad tym, kiedy zadanie powinno zostać uruchomione. Zmiany w systemie operacyjnym Android będą również ograniczać, gdy odbiorniki emisji będą działać lub rodzaje pracy, na które mogą reagować.

Istnieją dwie kluczowe funkcje wydajnego wykonywania pracy w tle (czasami określane jako zadanie w tle lub zadanie):

  1. Inteligentne planowanie pracy — ważne jest, aby aplikacja wykonywała pracę w tle, co robi jako dobry obywatel. Najlepiej, aby aplikacja nie wymagała uruchomienia zadania. Zamiast tego aplikacja powinna określić warunki, które muszą zostać spełnione, gdy zadanie może zostać uruchomione, a następnie zaplanować to zadanie z systemem operacyjnym, który wykona pracę po spełnieniu warunków. Dzięki temu system Android może uruchomić zadanie w celu zapewnienia maksymalnej wydajności na urządzeniu. Na przykład żądania sieciowe mogą być wsadowe do uruchamiania wszystkich w tym samym czasie, aby maksymalnie wykorzystać obciążenie związane z siecią.
  2. Hermetyzowanie pracy — kod do wykonania pracy w tle powinien być hermetyzowany w dyskretnym składniku, który można uruchomić niezależnie od interfejsu użytkownika i będzie stosunkowo łatwy do ponownego przygotowania, jeśli praca nie zostanie ukończona z jakiegoś powodu.

Harmonogram zadań systemu Android to struktura wbudowana w system operacyjny Android, który zapewnia płynny interfejs API umożliwiający uproszczenie planowania pracy w tle. Harmonogram zadań systemu Android składa się z następujących typów:

  • Jest Android.App.Job.JobScheduler to usługa systemowa używana do planowania, wykonywania i w razie potrzeby anulowania zadań w imieniu aplikacji systemu Android.
  • Jest Android.App.Job.JobService to abstrakcyjna klasa, która musi zostać rozszerzona o logikę, która będzie uruchamiać zadanie w głównym wątku aplikacji. Oznacza to, że JobService element jest odpowiedzialny za sposób asynchronicznego wykonywania pracy.
  • Obiekt Android.App.Job.JobInfo zawiera kryteria, które należy kierować systemem Android, gdy zadanie powinno zostać uruchomione.

Aby zaplanować pracę z harmonogramem zadań systemu Android, aplikacja platformy Xamarin.Android musi hermetyzować kod w klasie, która rozszerza klasę JobService . JobService ma trzy metody cyklu życia, które mogą być wywoływane w okresie istnienia zadania:

  • bool OnStartJob(Parametry JobParameters) — ta metoda jest wywoływana przez JobScheduler metodę do wykonania pracy i uruchamiana w głównym wątku aplikacji. Jest to odpowiedzialność JobService za asynchroniczne wykonywanie pracy i powrót true , jeśli istnieje praca pozostała, lub false jeśli praca jest wykonywana.

    Gdy JobScheduler wywoła tę metodę, będzie żądać i zachować wakelock z systemu Android na czas trwania zadania. Po zakończeniu zadania należy JobService poinformować JobScheduler o tym fakt przez wywołanie JobFinished metody (opisanej dalej).

  • JobFinished(Parametry JobParameters, bool needsReschedule) — ta metoda musi być wywoływana przez parametr , JobService aby poinformować JobScheduler , że praca jest wykonywana. Jeśli JobFinished nie zostanie wywołana, JobScheduler nie spowoduje to usunięcia blokady, co powoduje niepotrzebne opróżnienie baterii.

  • bool OnStopJob(Parametry JobParameters) — jest to wywoływane, gdy zadanie jest przedwcześnie zatrzymane przez system Android. Powinna zostać zwrócona true , jeśli zadanie powinno zostać ponownie ułożone na podstawie kryteriów ponawiania (omówione poniżej bardziej szczegółowo).

Istnieje możliwość określenia ograniczeń lub wyzwalaczy , które będą kontrolować, kiedy zadanie może lub powinno zostać uruchomione. Na przykład można ograniczyć zadanie tak, aby było uruchamiane tylko wtedy, gdy urządzenie jest ładowane, lub uruchamiać zadanie po zrobieniu zdjęcia.

W tym przewodniku szczegółowo omówiono sposób implementowania JobService klasy i planowania jej za pomocą klasy JobScheduler.

Wymagania

Harmonogram zadań systemu Android wymaga interfejsu API systemu Android na poziomie 21 (Android 5.0) lub nowszego.

Korzystanie z harmonogramu zadań systemu Android

Istnieją trzy kroki dotyczące korzystania z interfejsu API jobScheduler systemu Android:

  1. Zaimplementuj typ usługi JobService, aby hermetyzować pracę.
  2. JobInfo.Builder Użyj obiektu , aby utworzyć JobInfo obiekt, który będzie przechowywać kryteria uruchamiania JobScheduler zadania.
  3. Zaplanuj zadanie przy użyciu polecenia JobScheduler.Schedule.

Implementowanie usługi jobService

Wszystkie prace wykonywane przez bibliotekę harmonogramu zadań systemu Android muszą być wykonywane w typie rozszerzającym klasę abstrakcyjną Android.App.Job.JobService . Tworzenie elementu JobService jest bardzo podobne do tworzenia za Service pomocą platformy systemu Android:

  1. Rozszerz klasę JobService .
  2. Dekoruj podklasę za pomocą parametru ServiceAttribute i ustaw Name parametr na ciąg składający się z nazwy pakietu i nazwy klasy (zobacz poniższy przykład).
  3. Permission Ustaw właściwość w obiekcie na ServiceAttribute ciąg android.permission.BIND_JOB_SERVICE.
  4. Zastąpij metodę OnStartJob , dodając kod, aby wykonać pracę. System Android wywoła tę metodę w głównym wątku aplikacji w celu uruchomienia zadania. Praca, która potrwa dłużej niż kilka milisekund, powinna zostać wykonana w wątku, aby uniknąć blokowania aplikacji.
  5. Po zakończeniu JobService pracy metoda musi wywołać metodę JobFinished . Ta metoda informuje JobService o wykonaniu JobScheduler tej pracy. Brak wywołania JobFinished spowoduje JobService wprowadzenie niepotrzebnych wymagań na urządzeniu, skracając żywotność baterii.
  6. Dobrym pomysłem jest również zastąpienie OnStopJob metody . Ta metoda jest wywoływana przez system Android, gdy zadanie jest zamykane przed jego zakończeniem i zapewnia JobService możliwość prawidłowego usuwania wszystkich zasobów. Ta metoda powinna zwrócić true , jeśli konieczne jest ponowne zaplanowanie zadania lub false jeśli nie jest pożądane ponowne uruchomienie zadania.

Poniższy kod jest przykładem najprostszego JobService dla aplikacji, używając języka TPL do asynchronicznego wykonywania pewnych zadań:

[Service(Name = "com.xamarin.samples.downloadscheduler.DownloadJob", 
         Permission = "android.permission.BIND_JOB_SERVICE")]
public class DownloadJob : JobService
{
    public override bool OnStartJob(JobParameters jobParams)
    {            
        Task.Run(() =>
        {
            // Work is happening asynchronously
                      
            // Have to tell the JobScheduler the work is done. 
            JobFinished(jobParams, false);
        });

        // Return true because of the asynchronous work
        return true;  
    }

    public override bool OnStopJob(JobParameters jobParams)
    {
        // we don't want to reschedule the job if it is stopped or cancelled.
        return false; 
    }
}

Tworzenie informacji o zadaniu w celu zaplanowana zadania

Aplikacje platformy Xamarin.Android nie tworzą wystąpienia JobService bezpośrednio, zamiast tego przekażą JobInfo obiekt do JobSchedulerobiektu . Spowoduje JobScheduler to utworzenie wystąpienia żądanego JobService obiektu, zaplanowanie i uruchomienie JobService zgodnie z metadanymi w obiekcie JobInfo. Obiekt JobInfo musi zawierać następujące informacje:

  • JobId — jest int to wartość używana do identyfikowania zadania w obiekcie JobScheduler. Ponowne przy użyciu tej wartości spowoduje zaktualizowanie wszystkich istniejących zadań. Wartość musi być unikatowa dla aplikacji.
  • JobService — ten parametr to ComponentName parametr, który jawnie identyfikuje typ, którego JobScheduler należy użyć do uruchomienia zadania.

Ta metoda rozszerzenia pokazuje, jak utworzyć element JobInfo.Builder z systemem Android Context, na przykład Działanie:

public static class JobSchedulerHelpers
{
    public static JobInfo.Builder CreateJobBuilderUsingJobId<T>(this Context context, int jobId) where T:JobService
    {
        var javaClass = Java.Lang.Class.FromType(typeof(T));
        var componentName = new ComponentName(context, javaClass);
        return new JobInfo.Builder(jobId, componentName);
    }
}

// Sample usage - creates a JobBuilder for a DownloadJob and sets the Job ID to 1.
var jobBuilder = this.CreateJobBuilderUsingJobId<DownloadJob>(1);

var jobInfo = jobBuilder.Build();  // creates a JobInfo object.

Zaawansowaną funkcją harmonogramu zadań systemu Android jest możliwość kontrolowania, kiedy zadanie jest uruchamiane lub w jakich warunkach może zostać uruchomione zadanie. W poniższej tabeli opisano niektóre metody, które JobInfo.Builder umożliwiają aplikacji wywieranie wpływu na uruchomienie zadania:

Metoda opis
SetMinimumLatency Określa, że opóźnienie (w milisekundach), które należy zaobserwować przed uruchomieniem zadania.
SetOverridingDeadline Deklaruje, że zadanie musi zostać uruchomione przed upływem tego czasu (w milisekundach).
SetRequiredNetworkType Określa wymagania dotyczące sieci dla zadania.
SetRequiresBatteryNotLow Zadanie może być uruchamiane tylko wtedy, gdy urządzenie nie wyświetla użytkownikowi ostrzeżenia o "niskiej baterii".
SetRequiresCharging Zadanie może działać tylko wtedy, gdy bateria jest ładowana.
SetDeviceIdle Zadanie zostanie uruchomione, gdy urządzenie jest zajęte.
SetPeriodic Określa, że zadanie powinno być regularnie uruchamiane.
SetPersisted Zadanie powinno być perisistą w przypadku ponownych uruchomień urządzeń.

Zawiera SetBackoffCriteria kilka wskazówek dotyczących JobScheduler czasu oczekiwania przed ponownym uruchomieniem zadania. Istnieją dwie części kryteriów wycofywania: opóźnienie w milisekundach (wartość domyślna 30 sekund) i typ wycofywania, który powinien być używany (czasami określany jako zasady wycofywania lub zasady ponawiania). Te dwie zasady są hermetyzowane w wyliczeniem Android.App.Job.BackoffPolicy :

  • BackoffPolicy.Exponential — Zasady wycofywania wykładniczego zwiększą początkową wartość wycofywania wykładniczo po każdym niepowodzeniu. Podczas pierwszego niepowodzenia zadania biblioteka będzie czekać początkowy interwał określony przed ponownym zaplanowaniem zadania — na przykład 30 sekund. Po raz drugi zadanie zakończy się niepowodzeniem, biblioteka będzie czekać co najmniej 60 sekund przed próbą uruchomienia zadania. Po trzeciej nieudanej próbie biblioteka będzie czekać 120 sekund itd. Jest to wartość domyślna.
  • BackoffPolicy.Linear — Ta strategia jest liniowym wycofywaniem, które powinno zostać ponownie zaplanowane, aby zadanie było uruchamiane w ustalonych odstępach czasu (dopóki nie powiedzie się). Wycofywanie liniowe najlepiej nadaje się do pracy, która musi zostać ukończona tak szybko, jak to możliwe lub w przypadku problemów, które szybko się rozwiążą.

Aby uzyskać więcej informacji na temat tworzenia obiektu, przeczytaj dokumentację JobInfo firmy Google dotyczącą JobInfo.Builder klasy.

Przekazywanie parametrów do zadania za pomocą informacji o zadaniu

Parametry są przekazywane do zadania przez utworzenie przekazanego Job.Builder.SetExtras elementu PersistableBundle wraz z metodą :

var jobParameters = new PersistableBundle();
jobParameters.PutInt("LoopCount", 11);

var jobBuilder = this.CreateJobBuilderUsingJobId<DownloadJob>(1)
                     .SetExtras(jobParameters)
                     .Build();

Dostęp PersistableBundle do obiektu jest uzyskiwany z Android.App.Job.JobParameters.Extras właściwości w OnStartJob metodzie elementu JobService:

public override bool OnStartJob(JobParameters jobParameters)
{
    var loopCount = jobParams.Extras.GetInt("LoopCount", 10);
    
    // rest of code omitted
} 

Planowanie zadań

Aby zaplanować zadanie, aplikacja platformy Xamarin.Android uzyska odwołanie do JobScheduler usługi systemowej i wywoła JobScheduler.Schedule metodę z JobInfo obiektem utworzonym w poprzednim kroku. JobScheduler.Schedule natychmiast zwróci jedną z dwóch wartości całkowitych:

  • JobScheduler.ResultSuccess — zadanie zostało pomyślnie zaplanowane.
  • JobScheduler.ResultFailure — nie można zaplanować zadania. Jest to zwykle spowodowane przez parametry powodujące konflikt JobInfo .

Ten kod jest przykładem planowania zadania i powiadamiania użytkownika o wynikach próby planowania:

var jobScheduler = (JobScheduler)GetSystemService(JobSchedulerService);
var scheduleResult = jobScheduler.Schedule(jobInfo);

if (JobScheduler.ResultSuccess == scheduleResult)
{
    var snackBar = Snackbar.Make(FindViewById(Android.Resource.Id.Content), Resource.String.jobscheduled_success, Snackbar.LengthShort);
    snackBar.Show();
}
else
{
    var snackBar = Snackbar.Make(FindViewById(Android.Resource.Id.Content), Resource.String.jobscheduled_failure, Snackbar.LengthShort);
    snackBar.Show();
}

Anulowanie zadania

Istnieje możliwość anulowania wszystkich zaplanowanych zadań lub tylko jednego zadania przy użyciu JobsScheduler.CancelAll() metody lub JobScheduler.Cancel(jobId) metody:

// Cancel all jobs
jobScheduler.CancelAll(); 

// to cancel a job with jobID = 1
jobScheduler.Cancel(1)

Podsumowanie

W tym przewodniku omówiono sposób użycia harmonogramu zadań systemu Android do inteligentnego wykonywania pracy w tle. Omówiono w nim sposób hermetyzacji pracy, która ma być wykonywana jako element JobService i jak używać JobScheduler elementu do planowania tej pracy, określając kryteria za pomocą elementu JobTrigger oraz sposób obsługi błędów za pomocą elementu RetryStrategy.