Programování pro iOS - 23. Jak se aktivovat, jsme-li v pozadí - MujMAC.cz - Apple, Mac OS X, Apple iPod

Odběr fotomagazínu

Fotografický magazín "iZIN IDIF" každý týden ve Vašem e-mailu.
Co nového ve světě fotografie!

 

Zadejte Vaši e-mailovou adresu:

Kamarád fotí rád?

Přihlas ho k odběru fotomagazínu!

 

Zadejte e-mailovou adresu kamaráda:

Soutěž

Sponzorem soutěže je:

IDIF

 

Jaký fotograf/ka získal/a cenu za nejpopulárnější příspěvek v Nikon Photo Contest?

V dnešní soutěži hrajeme o:

Seriály

Více seriálů



Software

Programování pro iOS - 23. Jak se aktivovat, jsme-li v pozadí

5. ledna 2011, 00.00 | Známe už základy hry, kterou s námi iOS hraje a víme, jak si vyžádat trochu více času ve chvíli, kdy je aplikace odsunuta z obrazovky. Co ale v případě, kdy bychom ji potřebovali aktivovat o něco později?

V minulých dílech seriálu jsme se zabývali "multitaskingem" (ve skutečnosti jde spíše o sadu konvencí, pravidel a podmínek, za nichž systém aplikaci dovolí běžet na pozadí).

Restík od minula

V předchozím dílu jsme si ukázali kromě jiného chování aplikace, jež zablokuje hlavní vlákno a řekli jsme si, že v takové aplikaci poběží jak toto hlavní vlákno, tak i všechna vlákna ostatní, deset sekund po její deaktivaci – pak je násilně ukončena. Zmínili jsme se ale také o tom, že neběží časovač, aktivovaný kódem

-(BOOL)application:(UIApplication*)application
  didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
...
    [NSTimer scheduledTimerWithTimeInterval:.1
        target:self selector:@selector(timer)
        userInfo:nil repeats:YES];
...
    return YES;
}

a slíbili jsme si, že pro ty čtenáře – existují-li vůbec tací –, již snad náhodou nevědí, proč tomu tak je, dnes uvedeme vysvětlení.

To je velmi prosté: časovače založené na službách třídy NSTimer a podobných (speciálně tedy také odložené odesílání zpráv, aktivované standardní metodou performSelector:withObject:afterDelay:, jež není ničím jiným, než pohodlným API nad NSTimerem) jsou založeny na event loopu. Timer nedělá nic jiného, než že si od správce event loopu vyžádá vložení události, jež nastane v předem definovaném čase.

Event loop je ovšem součástí hlavního vlákna* a běží v jeho rámci. Je-li hlavní vlákno blokováno, event loop tedy neběží, a časovače fungovat nemohou.

* Přesněji řečeno, každé vlákno má vlastní event loop; pokud bychom tedy NSTimer použili v jiném vláknu, např. takto:

-(BOOL)application:(UIApplication*)application
  didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
    [[[NSOperationQueue alloc] init] addOperationWithBlock:^{
        [NSTimer scheduledTimerWithTimeInterval:.1
            target:self selector:@selector(timer)
            userInfo:nil repeats:YES];
        [[NSRunLoop currentRunLoop] run]; //*
    }];
    return YES;
}

kde řádek označený hvězdičkou je nutný pro spuštění event loopu v druhém vláknu (systém sám se postará o spuštění event loopu samozřejmě jen v tom hlavním), poběží právě tehdy, pokud toto vlákno není blokované. To ale spíše jen pro úplnost; v praxi se časovače v jiném vláknu než v hlavním využívají jen zřídkakdy.

Lokální notifikace

Tzv. "lokální notifikace" jsou jediným způsobem, jak si může aplikace sama programově vyžádat, aby po případném suspendování byla probuzena v požadovaný čas. Není to samo o sobě nijak komplikované (ačkoli na řadu různých háčků narazíme později) – prostě jen v programu vytvoříme objekt třídy UILocalNotification, nastavíme požadovaný čas a případné další atributy. Typicky by to mohlo vypadat kupříkladu takto:

UILocalNotification *ln=[[[UILocalNotification alloc] init]
          autorelease];
ln.fireDate=[NSDate dateWithTimeIntervalSinceNow:...];
ln.alertBody=@"Launch MyGreatApplication";
[[UIApplication sharedApplication] scheduleLocalNotification:ln];

Po uplynutí zadaného času... nastanou výše zmíněné komplikace. To proto, že systém interpretuje zadanou notifikaci různě za různých podmínek, a my ji proto také musíme různým způsobem v aplikaci ošetřit.

Aplikace je na popředí

Pokud náhodou zadaný čas vyprší ještě v době, kdy je aplikace aktivní a "má obrazovku", pošle se pouze aplikačnímu delegátu zpráva

-(void)application:(UIApplication*)application
  didReceiveLocalNotification:(UILocalNotification*)notification;

jejímž argumentem je samozřejmě dříve vytvořený objekt třídy UILocalNotification. Pokud bychom tuto zprávu v aplikačním delegátu vůbec neimplementovali, nestane se nic, a o notifikaci se vůbec nedozvíme.

Jinak se nic neděje; chceme-li na notifikaci nějak reagovat, musíme to zajistit programově. V tomto případě se tedy lokální notifikace chová v zásadě stejně, jako NSTimer, a nic jiného nám nenabídne.

Aplikace běží nebo je suspendována v pozadí

Toto je hlavní situace, pro niž jsme vůbec k notifikaci sáhli: pokud aplikace buď běží na pozadí, nebo je suspendována, systém umožňuje ji na základě notifikace opět aktivovat (což jinak není možné: i pokud aplikace na pozadí běží, sama se programově aktivovat nemůže).

Ale... není to tak jednoduché.

Operační systém iOS je totiž sestaven na základě ne vždy zcela šťastné (a obecně nesplnitelné, samozřejmě) představy "aplikace, která není v popředí, nemůže udělat nic, o čem by uživatel nevěděl". Důsledkem tohoto přístupu je, že ve chvíli, kdy čas lokální notifikace vyprší, systém zobrazí alert s textem, který jsme při sestavení notifikace uložili do jejího atributu alertBody a s tlačítky "Close" a "View" (obsah toho druhého můžeme, chceme-li, změnit pomocí atributu alertAction).

Pokud uživatel zvolí "Close", naše aplikace zůstane suspendována a o ničem se ani nedozví.

Pokud uživatel zvolí "View" (nebo text, který jsme uložili do atributu alertAction), teprve systém naši aplikaci probudí. Pokud jsme implementovali v delegátu aplikace metodu application:didReceiveLocalNotification:, dostane odpovídající zprávu, jejímž argumentem bude patřičný objekt třídy UILocalNotification, po zprávě applicationDidBecomeActive:.

A co se stane, pokud ponecháme atribut alertBody prázdný?

Nic. Žádný alert se nezobrazí, takže uživatel nemůže stisknout tlačítko "View", a naše aplikace nebude aktivována.

Zde je ještě speciální možnost nastavit atribut applicationIconBadgeNumber na nějaké číslo; v takovém případě se přec jen něco málo stane – aplikační ikona bude opatřena tímto číslem v červeném kroužku – podobně jako např. počet zpráv v aplikaci Mail (takže lze předpokládat, že uživatel asi dříve nebo později aplikaci aktivuje, aby se podíval, co to má znamenat). Automatická aktivace ale nenastane ani v tomto případě.

Aplikace neběží

Poslední situace, již musíme speciálně ošetřit, je, pokud naše aplikace v době, kdy dojde k aktivaci lokální notifikace, vůbec neběží.

Operační systém iOS je naštěstí dostatečně chytrý k tomu, aby zobrazil stejný alert (nebo opatřil aplikační ikonu stejným číslem), a v případě, kdy po zobrazení alertu uživatel zvolí zvolí "View" (nebo text, který jsme uložili do atributu alertAction), systém naši aplikaci spustí. To je podstatné, protože naše aplikace mohla být v suspendovaném stavu kdykoli bez našeho vědomí ukončena, pokud systém potřeboval více paměti.

Drobný podraz, který musíme ošetřit, však spočívá v tom, že v tomto případě nedostaneme zprávu application:didReceiveLocalNotification:; namísto toho je notifikace uložena v argumentu launchOptions standardní zprávy application:didFinishLaunchingWithOptions: pod klíčem UIApplicationLaunchOptionsLocalNotificationKey.

Obsah seriálu (více o seriálu):

Tématické zařazení:

 » Rubriky  » Informace  

 » Rubriky  » Agregator  

 » Rubriky  » Tipy a Triky  

 » Rubriky  » Začínáme s  

 » Rubriky  » Software  

Poslat článek

Nyní máte možnost poslat odkaz článku svým přátelům:

Váš e-mail:

(Není povinný)

E-mail adresáta:

Odkaz článku:

Vzkaz:

Kontrola:

Do spodního pole opište z obrázku 5 znaků:

Kód pro ověření

 

 

 

 

 

Přihlášení k mému účtu

Uživatelské jméno:

Heslo: