WinRT chyták - "one shot" Timer BackgroundTask se neodregistruje po jeho "one shot"

Development WinUI

8 years ago

Nejprve - omlouvám všem fanouškům Jacka Reachera, tento článek není o jedné z knih této série, leč jeho název ji obsahuje rovnou dvakrát :-) . V mé poslední vydané Windows 10 aplikaci Event Countdowns jsem udělal nešťastnou chybu. Při registraci Background Tasku který měl aktualizovat dlaždice aplikace každých 30 minut jsem použil následující kód:

string myTaskName = "TileUpdaterBackgroundTask";

// check if task is already registered
var task =
    BackgroundTaskRegistration.AllTasks.Where( cur => cur.Value.Name == myTaskName ).Select( c => c.Value ).SingleOrDefault();
if ( task != null )
{
    //do not register again                    
    return;
}

var backgroundAccess = await BackgroundExecutionManager.RequestAccessAsync();

if ( backgroundAccess == BackgroundAccessStatus.AllowedMayUseActiveRealTimeConnectivity ||
    backgroundAccess == BackgroundAccessStatus.AllowedWithAlwaysOnRealTimeConnectivity )
{
    // register a new task
    BackgroundTaskBuilder taskBuilder = new BackgroundTaskBuilder
    {
        Name = myTaskName,
        TaskEntryPoint = "TileUpdateTask.TileUpdateBackgroundTask"
    };

    taskBuilder.SetTrigger( new TimeTrigger( 30, true ) );

    BackgroundTaskRegistration myFirstTask = taskBuilder.Register();
}

Na první pohled nic neobvyklého. Nebo - to jsem si alespoň myslel. Den po vydání aplikace jsem se podíval na připnutou dlaždici odpočítávající čas do premiéry filmu Captain America: Civil War a s velkým překvapením zjistil, že zobrazený zbývající čas je byl o den posunutý! Okamžitě jsem zkontroloval zdrojový kód a po několika minutách zběsilého debugování jsem objevil skryté zlo - druhý parametr konstruktoru třídy TimeTrigger.

taskBuilder.SetTrigger( new TimeTrigger( 30, true ) );

Druhý parametr, oneShot, určuje, zda task bude spuštěn pouze jednou (hodnota true) nebo periodicky (hodnota false). Moje naivní řešení tedy spočívalo ve výměně true za false a protože na počítači aplikace fungovala bez problémů, publikoval jsem aktualizovanou verzi na Store. Když nová verze aplikace dorazila na můj telefon, s radostí jsem ji nainstaloval, spustil a čekal, až dlaždice znovu oživnou. A čekal jsem. A čekal jsem. A... nic. Byl jsem zmaten a nechápal jsem, kde může být rozdíl mezi počítačem a telefonem. Tak jsem se znovu vrátil ke zdrojovému kódu a znovu jej promyslel. A pak - jsem to uviděl.

// check if task is already registered
var task =
    BackgroundTaskRegistration.AllTasks.Where( cur => cur.Value.Name == myTaskName ).Select( c => c.Value ).SingleOrDefault();
if ( task != null )
{
    //do not register again
    return;
}

Přirozeně jsem předpokládal, že jakmile oneShot Background Task je jednou spuštěn, systém jej automaticky odregistruje. Ve skutečnosti tomu tak ale není.

I poté co oneShot trigger spustí Background Task, task stejně zůstane zaregistrován!

To znamená, že po instalaci aktualizované verze aplikace na můj telefon a jejím spuštění se při pokusu o registraci tasku kód zastavil na kontrole podmínky task != null která se vyhodnotila jako true, protože task byl stále zaregistrován, přestože již proběhl! Finální řešení tedy vyžadovalo vynutit odregistrování Background Tasku a jeho znovuzaregistrování s oneShot nastaveným na false. Bug je odchycen a dlaždice znovu žijí!