Skip to main content

Background task in Windows 8 Metro Style App

O aplicatie nativa Metro style este diferita din unele puncte de vedere fata de o aplicatie desktop pentru Windows 7 de exemplu. Daca ati scris aplicatii pentru Windows Phone 7 va pot spune ca din unele puncte de vedere o aplicatie Metro style este mai asemanatoare cu o aplicatie Silverlight pentru Windows Phone 7.
O aplicatie in Metro style poate sa aibe doua stari. Prima stare este starea foreground, cand aplicatia este cea curenta (cea care se afiseaza pe ecran) si ruleaza ca o aplicatie normala. A doua stare este cand aceasta se afla in spate. Nu este aplicatie curenta. In momentul acesta toate firele de executie (thread-urile) sunt inghetate. Aceasta stare poarta numele de suspended. Momentul cand se face switch intre cele doua stari poate sa fie prins de catre programator. Despre cum se face acest lucru o sa vorbim cu alta ocazie.
Astazi o sa vorbim despre cum putem sa executa cod in fundal, cand aplicatia noastra este oprita. Uneori avem nevoie la aplicatie sa putem afisa updateuri pe lock screen sau sa verificam daca pe un anumit socket se intampla ceva (VOIP). Pentru acest lucru au fost creat background task.
Un lucru foarte important care trebuie stiut de la inceput despre background task este ca acesta ruleaza cand sistemul de operare doreste nu cand doreste programatorul. Exista mai multe triggere pe care le putem folosii cand vrem ca un background task sa fie apelat. Prin intermediul acestora codul nostru o sa poata sa fie executat.
Mai jos gasiti lista triggerele care sunt disponibile in acest moment:
  1. ControlChannelTrigger
  2. InternetAvailable
  3. InternetNotAvailable
  4. LockScreenApplicationAdded
  5. LockScreenApplicationRemoved
  6. MaintenanceTrigger
  7. NetworkNotificationChannelReset
  8. NetworkStateChange
  9. OnlineIdConnectedStateChange
  10. PushNotificationTrigger
  11. ServicingComplete
  12. SessionConnected
  13. SessionDisconnected
  14. SessionStart
  15. SmsReceived
  16. TimeTrigger
  17. TimeZoneChange
  18. UserAway
  19. UserPresent
Ne putem imagina ca un background task este ca o combinatie intre un Windows Service si un Windows Task. Are putin din fiecare. Un background task poate sa ruleze in doua "locatii". In functie de cum este seteaza userul aplicatia noastra:
  • Lock Screen App
  • Non-Lock Screen App
Este foarte important sa stim ca in functie de acest lucru, Windows 8 ne aluca un numar limitat de resurse pe care le putem folosii (CPU). In cazul unei aplicatii Lock Screen App, ne sunt alocate 2 secunde de CPU la un interval de 15 minute. In celalat caz ne este alocata o singura secunda la interval de 120 de minute. Atentie, 1 secunda CPU nu este tot una cu 1 secunda normala. De exemplu daca facem un request spre un URL, nu se contorizeaza timpul pana rezultatul vine de la server. La fel exista anumite limitari din punct de vedere a traficului pe care il putem face prin WiFi.
As vrea sa subliniez ca aceste valori s-ar putea sa se schimbe in viitor, din aceasta cauza este bine sa verificati de fiecare data cand un nou release sau update se face la Windows 8 (noi cel putin speram sa se schimbe putin). De exemplu un trigger de tip TimeTrigger poate sa ruleze la un interval de cel putin 15 minute. Asta inseamna ca daca avem un backgroud task care dorim sa fie rulat odata la 2 minute, nu o sa putem face acest lucru sub nici o forma.
Din punct de vedere tehnic, un background task se definieste intr-un assembly separat si trebuie inregistrat. Trebuie avut grija ca tipul de clas library selectat sa fie WinMD si nu DLL. In functe de stare pe care o are aplicatia noastra un background task poate sa fie apelat de catre:
  • aplicatia noastra - cand aplicatia ruleaza si este in foreground
  • Windows 8 - cand aplicatia este suspended sau aplicatia nu ruleaza (terminated)
Un background task poate sa aibe una sau mai multe condititi care sa fie verificare inainte sa fie rulat. De exemplu daca avem un backgroud task care are nevoie de conexiuen de internet nu dorim sa il rulam daca nu avem conexiune. In acest caz putem sa specificam o conditie care sa fie verificata inainte ca task-ul sa fie apelat.
Pentru a inregistra un task este nevoie sa ne instantiam un BackgroundTaskBuilder si sa setam triggerul si conditiile (optional) cand un background task sa ruleze. Dupa acest pas, putem sa apelam metoda Register() care o sa ne inregistreze background task-ul.
BackgroundTaskBuilder builder = new BackgroundTaskBuilder();
builder.Name = "FooBackgroundTask";    
builder.TaskEntryPoint = "Foo.FooBackgroundTask";   
IBackgroundTrigger trigger = new TimeTrigger(30, true);
builder.SetTrigger(trigger);
IBackgroundTaskRegistration task = builder.Register();
TaskEntryPoint specifica namespaceul si clasa unde clasa noastra este definita.
Exista doua evenimente, pe baza carora din aplicatia putem sa stim care este progresul la task si cand s-a terminat de executat. Merita de mentionat ca este nevoie sa ne inrefitram la aceste evenimente de fiecare data cand aplicatia se porneste. Cand aceasta trece din starea suspended in running nu mai este nevoie sa ne inregistram la aceste evenimente. Prin intermediul metodei BackgroundTaskRegistration.AllTasks putem sa obtinem toate task-urile inregistrate. Dupa ce obtinem task-ul pe care noi il dorim putem sa ne inregistram la evenimente.

var fooBT = BackgroundTaskRegistration.AllTasks.First(x=>x.Name == "FooBackgroundTask");
fooBT.Value.Progress += new BackgroundTaskProgressEventHandler(OnProgress);
fooBT.Value.Completed += new BackgroundTaskCompletedEventHandler(OnCompleted);
Acuma ca am vazut putem sa manipulam un background task, este momentul sa vedem cum il putem definii.
Clasa noastra FooBackgroundTask, trebuie sa implementeze interfata IBackgroundTask. Aceasta are o singura metoda Run, care este apelata de catre Windows 8 cand task-ul este pornit.
public sealed class FooBackgroundTask:IBackgroundTask
   {
       private int globalcount;

       void IBackgroundTask.Run(IBackgroundTaskInstance taskInstance)
       {
               // do something
       }
   }
In interiorul metodei Run se intampla toata magia, putem sa facem orice, atata timp cat ne incadram in CPU time alocat. Prin intermediul taskInstance, care este de tip IBackgroundTaskInstance, putem sa comunicam cu aplicatia noastra care este in foreground. De exemplu putem sa trimitem notificari de tipul progress updateds.
Mai devreme am spus ca un background task poate sa faca apeluri asincrone spre orice alte resurse. Pentru a putea face acest lucru este nevoie sa folosim BackgroundTaskDeferral. Pentru fiecare operatie asincrona ar trebui sa folosim unul nou.
BackgroundTaskDeferral deferral = taskInstance.GetDeferral();
IAsyncAction action = //Call async
action.Completed = new AsyncActionCompletedHandler(  
   (IAsyncAction act, AsyncStatus stat) =>
           {
               deferral .Complete();
           });
Pentru a putea folosii un background task o aplicatie Metro style trebuie sa il declare in manifest. Fiecare background task o sa fie un alt entry point. O aplicatie poate sa aibe mai mult de un background task. Prin manifest se specifica un background task ca si o extensie la care se defineste un entry point si tipul de eveniment pentru trigger. Daca din cod se incearca inregistrarea la un alt tip de trigger, aplicatia o sa crape.
Iar mai jos puteti sa vedeti o schema care prezinta cum este lansat un background task si de catre cine.

Cam asta a fost despre background task. Inainte sa va apucati sa scrieti un background task pentru aplicatia voastra ar trebui sa va ganditi de doua ori daca aveti nevoie de unul si ar trebui sa tineti seama de urmatoarele recomandari (pe care le puteti gasii si pe MSDN):
  • Design background tasks to be short lived.
  • Design the lock screen user experience as described in the “Guidelines and checklists for lock screen tiles.”
  • Use BackgroundTaskHost.exe as the executable for background tasks.
  • Describe the background task class name or JavaScript file name accurately in the manifest.
  • Use persistent storage to share data between the background task and the app.
  • Register for progress and completion handlers in the app.
  • Register for a background task cancellation handler in the background task class.
  • Register for the ServicingComplete trigger if you expect to update the app.
  • Ensure that the background task class library is referenced in the main project and its output type is winmd.
  • Describe the triggers in the background manifest accurately.
  • Verify if the app needs to be on the lock screen.
  • Do not display UI other than toast, tiles or badges from a background task.
  • Do not rely on user interaction in background tasks.

Comments

  1. Interesant - cam aceeasi filozofie ca la WP7. Exista ceva similar cu push notification service?

    ReplyDelete
  2. Da, push notification service exista si pe Windows 8. Este foarte simular cu cel de pe WP8.
    Un demo despre push notification pe W8 poate sa fie gasit la adresa de mai jos:
    http://code.msdn.microsoft.com/windowsapps/Push-and-periodic-de225603

    ReplyDelete

Post a Comment

Popular posts from this blog

Windows Docker Containers can make WIN32 API calls, use COM and ASP.NET WebForms

After the last post , I received two interesting questions related to Docker and Windows. People were interested if we do Win32 API calls from a Docker container and if there is support for COM. WIN32 Support To test calls to WIN32 API, let’s try to populate SYSTEM_INFO class. [StructLayout(LayoutKind.Sequential)] public struct SYSTEM_INFO { public uint dwOemId; public uint dwPageSize; public uint lpMinimumApplicationAddress; public uint lpMaximumApplicationAddress; public uint dwActiveProcessorMask; public uint dwNumberOfProcessors; public uint dwProcessorType; public uint dwAllocationGranularity; public uint dwProcessorLevel; public uint dwProcessorRevision; } ... [DllImport("kernel32")] static extern void GetSystemInfo(ref SYSTEM_INFO pSI); ... SYSTEM_INFO pSI = new SYSTEM_INFO(

ADO.NET provider with invariant name 'System.Data.SqlClient' could not be loaded

Today blog post will be started with the following error when running DB tests on the CI machine: threw exception: System.InvalidOperationException: The Entity Framework provider type 'System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer' registered in the application config file for the ADO.NET provider with invariant name 'System.Data.SqlClient' could not be loaded. Make sure that the assembly-qualified name is used and that the assembly is available to the running application. See http://go.microsoft.com/fwlink/?LinkId=260882 for more information. at System.Data.Entity.Infrastructure.DependencyResolution.ProviderServicesFactory.GetInstance(String providerTypeName, String providerInvariantName) This error happened only on the Continuous Integration machine. On the devs machines, everything has fine. The classic problem – on my machine it’s working. The CI has the following configuration: TeamCity .NET 4.51 EF 6.0.2 VS2013 It see

Navigating Cloud Strategy after Azure Central US Region Outage

 Looking back, July 19, 2024, was challenging for customers using Microsoft Azure or Windows machines. Two major outages affected customers using CrowdStrike Falcon or Microsoft Azure computation resources in the Central US. These two outages affected many people and put many businesses on pause for a few hours or even days. The overlap of these two issues was a nightmare for travellers. In addition to blue screens in the airport terminals, they could not get additional information from the airport website, airline personnel, or the support line because they were affected by the outage in the Central US region or the CrowdStrike outage.   But what happened in reality? A faulty CrowdStrike update affected Windows computers globally, from airports and healthcare to small businesses, affecting over 8.5m computers. Even if the Falson Sensor software defect was identified and a fix deployed shortly after, the recovery took longer. In parallel with CrowdStrike, Microsoft provided a too