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

How to check in AngularJS if a service was register or not

There are cases when you need to check in a service or a controller was register in AngularJS.
For example a valid use case is when you have the same implementation running on multiple application. In this case, you may want to intercept the HTTP provider and add a custom step there. This step don’t needs to run on all the application, only in the one where the service exist and register.
A solution for this case would be to have a flag in the configuration that specify this. In the core you would have an IF that would check the value of this flag.
Another solution is to check if a specific service was register in AngularJS or not. If the service was register that you would execute your own logic.
To check if a service was register or not in AngularJS container you need to call the ‘has’ method of ‘inhector’. It will return TRUE if the service was register.
if ($injector.has('httpInterceptorService')) { $httpProvider.interceptors.push('httpInterceptorService&#…

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.51EF 6.0.2VS2013
It seems that there …

Run native .NET application in Docker (.NET Framework 4.6.2)

Scope
The main scope of this post is to see how we can run a legacy application written in .NET Framework in Docker.

Context
First of all, let’s define what is a legacy application in our context. By a legacy application we understand an application that runs .NET Framework 3.5 or higher in a production environment where we don’t have any more the people or documentation that would help us to understand what is happening behind the scene.
In this scenarios, you might want to migrate the current solution from a standard environment to Docker. There are many advantages for such a migration, like:

Continuous DeploymentTestingIsolationSecurity at container levelVersioning ControlEnvironment Standardization
Until now, we didn’t had the possibility to run a .NET application in Docker. With .NET Core, there was support for .NET Core in Docker, but migration from a full .NET framework to .NET Core can be costly and even impossible. Not only because of lack of features, but also because once you…