Mobilná aplikácia bez signálu: ako navrhnúť dáta a synchronizáciu

Lokálne dáta, front zmien, opakované odosielanie a konflikty. Technický návrh aplikácie, ktorá po výpadku spojenia nestratí používateľovu prácu.

Človek používa mobilný telefón pri pracovnom stole

Najprv určite, ktoré úlohy musia fungovať offline

Predstavme si obecný kalendár a formulár na nahlásenie poškodenej lavičky. Človek otvorí termín zberu odpadu v garáži bez signálu, neskôr odfotí lavičku a napíše podnet. Kalendár môže ukázať poslednú stiahnutú verziu. Podnet musí zostať v telefóne aj po zatvorení aplikácie. Potvrdenie prijatia na úrade však aplikácia zobrazí až po odpovedi servera. Tieto tri pravidlá treba dohodnúť pred návrhom obrazoviek.

Android dokumentácia oddeľuje lokálny a sieťový zdroj dát a odporúča čítanie pre rozhranie cez lokálny zdroj. To samo osebe neurčuje, ktoré zápisy dovolíme bez siete. Pri každej úlohe preto zapíšeme požadovanú aktuálnosť, správanie pri prázdnej pamäti a následok oneskorenia. Cena synchronizácie závisí viac od týchto pravidiel než od vybranej knižnice.

ÚlohaBez spojeniaPo obnovení siete
Čítať kalendárPosledná uložená verzia a čas aktualizácieStiahnuť zmeny
Vytvoriť podnetUložiť rozpracovaný alebo čakajúci podnetOdoslať a uložiť potvrdenie
Potvrdiť rezerváciuZobraziť potrebu spojeniaOveriť dostupnosť na serveri

Rozhranie potrebuje pravdivý stav dát

Lokálna databáza môže obsahovať publikovaný obsah, rozpracované vstupy aj front neodoslaných operácií. Každý typ má vlastný životný cyklus. Oznam uložený včera nie je rovnaký stav ako podnet čakajúci na prijatie. Pri podnete pomôžu stavy rozpracovaný, čaká na odoslanie, odosiela sa, prijatý a vyžaduje opravu. Globálna ikonka spojenia tieto rozdiely nevysvetlí.

Zobrazenie načítaného obsahu oddelíme od informácie, že aplikácia práve kontroluje novšiu verziu. Pri chybe zostanú použiteľné staršie dáta a pribudne zrozumiteľný čas posledného úspešného načítania. Pri prvom spustení bez uloženého obsahu ukážeme osobitný prázdny stav. Používateľ si potom nepomýli prázdny kalendár s kalendárom bez udalostí ani lokálne uloženie formulára s úspešným podaním.

Zmenu a jej odoslanie uložte v jednej transakcii

Po stlačení tlačidla uložiť zapíšeme podnet a položku do odosielacieho frontu v jednej databázovej transakcii. Ak proces skončí medzi dvoma samostatnými zápismi, podnet môže existovať bez úlohy na odoslanie. Transakcia tento medzistav odstráni. SQLite poskytuje atomické transakcie za dokumentovaných podmienok; aplikácia stále musí riešiť plné úložisko a chyby zápisu a nesmie používateľovi potvrdiť neúspešné uloženie.

Operácia dostane stabilné ID, ktoré sa pri ďalšom pokuse nemení. Server musí toto ID vyhodnocovať v rozsahu prihláseného účtu alebo organizácie a uchovať výsledok už spracovaného zápisu. Ukážka opisuje vlastný model frontu, nie univerzálny sieťový protokol. Pri fotografii uložíme aj trvalý odkaz na miestny súbor a samostatne evidujeme, či sa príloha už preniesla.

Ilustračný záznam lokálneho frontu; názvy polí sú súčasťou modelového návrhu.json
{
  "operationId": "op-7d2c",
  "entityId": "report-84",
  "kind": "create-report",
  "baseVersion": null,
  "payload": {
    "category": "street-furniture",
    "description": "Damaged bench"
  },
  "attachment": "local://photo-84",
  "state": "pending",
  "attempts": 0
}

Opakovanie požiadavky nesmie vytvoriť druhý podnet

Najnepríjemnejší výpadok nastane po úspešnom zápise na serveri a pred prijatím odpovede v telefóne. Aplikácia nevie, či sa podnet prijal. Pri ďalšom pokuse odošle rovnakú operáciu. Server pod tým istým ID vráti pôvodný výsledok, takže občan ani pracovník úradu nedostanú druhý podnet. Identifikátor bez serverovej kontroly duplicity túto vlastnosť nezabezpečí.

Dočasné chyby siete alebo preťaženie zopakujeme s rastúcim odstupom a náhodným rozptylom. Neplatný vstup presunieme do stavu vyžadujúceho opravu; neplatné prihlásenie vyžaduje obnovenie relácie. Pri odhlasovaní si dohodneme, či rozpracované dáta ostanú pre pôvodný účet, alebo ich aplikácia odstráni. Nový účet nesmie prevziať cudzie čakajúce zápisy. Poradie operácií zachováme tam, kde druhý krok závisí od výsledku prvého.

Konflikt je rozhodnutie o dátach, nie technická drobnosť

Ak dve zariadenia upravia ten istý záznam, posledný zápis môže prepísať prácu druhého človeka. Pravidlo last write wins je jednoduché, ale čas telefónu navyše nemusí byť správny. Pri nekritickej preferencii môže byť prepísanie prijateľné. Pri popise podnetu, schválení alebo priradení úlohy potrebujeme vedieť, z ktorej verzie úprava vychádzala.

Server môže vydať ETag a požadovať hlavičku If-Match pri úprave. RFC 9110 definuje túto podmienku na kontrolu aktuálnej reprezentácie. Nesúlad umožní odmietnuť zastaraný zápis. Aplikácia potom načíta aktuálny záznam a ponúkne porovnanie alebo opakovanie upravenej požiadavky. Automatické zlúčenie má zmysel iba pri poliach s jasným pravidlom: dve nezávislé poznámky sa dajú pridať, dve rôzne schválenia nemožno spojiť bez znalosti procesu.

Synchronizujte aj odstránenia a návrat po dlhom výpadku

Prenos celého zoznamu pri každom otvorení obrazovky môže byť jednoduchší než vlastný protokol zmien. Pri väčších dátach si server a klient môžu odovzdávať nepriehľadný kurzor. Stránku zmien a nový kurzor zapíšeme spolu; po páde potom nepreskočíme nezapísané položky. Klient musí vedieť spracovať aj označenie odstráneného záznamu, inak sa starý oznam bude vracať z lokálnej pamäte.

Kurzor nesmie mať neobmedzenú životnosť iba preto, že aplikácia čaká mesiace na spustenie. Dohodneme obnovu celého stavu pri jeho expirácii. Pred touto obnovou ochránime neodoslané používateľské vstupy, aby ich čistenie stiahnutého obsahu nezmazalo. Schému lokálnej databázy verziujeme a migračný test spustíme aj s čakajúcimi operáciami. Nová verzia aplikácie musí rozumieť frontu vytvorenému tou predchádzajúcou.

Operačný systém neurčuje presný čas synchronizácie

Android WorkManager vie zachovať plánované úlohy cez reštart aplikácie a zariadenia, jeho spustenie však podlieha podmienkam a obmedzeniam systému. Na iOS Apple negarantuje doručenie tichých background notifikácií a môže ich obmedziť. Z toho pri návrhu vyplýva, že aplikácia má overovať dáta aj po návrate do popredia a ponúknuť používateľovi ručné obnovenie.

Požiadavka synchronizovať každú minútu sa musí porovnať s dostupnými mechanizmami, batériou a objemom dát. Čítanie oznamov, odoslanie fotografie a časovo citlivá rezervácia nepotrebujú rovnaký režim. Front je preto trvalý záznam práce; systémový plánovač iba poskytuje príležitosť túto prácu vykonať. V rozhraní ukážeme čakajúci podnet aj po dlhom odložení a pri otvorení aplikácie sa ho pokúsime odoslať podľa aktuálneho prihlásenia.

Otestujte miesta, kde aplikácia môže stratiť prácu

Testovanie spustíme medzi jednotlivými krokmi, nie iba pred kliknutím na uložiť. Odpojíme sieť po odoslaní požiadavky, ukončíme proces po lokálnom potvrdení a zmeníme serverový záznam počas offline úpravy. Pri každom scenári určíme očakávaný počet záznamov, stav frontu a text, ktorý uvidí používateľ. Nasledujúce prípady sú návrh testov, nie tvrdenie o vykonaných meraniach konkrétneho projektu.

  • Server prijme podnet, odpoveď sa stratí: opakovanie s tým istým ID vytvorí spolu jeden podnet a aplikácia získa jeho potvrdenie.
  • Telefón sa reštartuje s čakajúcou fotografiou: text aj súbor ostanú dostupné a odoslanie nadviaže bez druhej prílohy.
  • Druhý účet sa prihlási na rovnakom zariadení: neuvidí podnet ani front pôvodného používateľa.
  • Dve zariadenia upravia tú istú verziu: server a rozhranie použijú dohodnuté pravidlo konfliktu bez tichého zmiznutia zmeny.
  • Kurzor vyprší a aplikácia aktualizuje databázu: úplné načítanie zachová neodoslaný podnet, odstráni starý obsah a uloží nový kurzor.

Zdroje a dokumentácia

Pri implementácii skontrolujte dokumentáciu verzie, ktorú používate.

Od témy ku konkrétnemu riešeniu.

Súvisiaca realizácia: Obec Smolenice

Máte proces,
ktorý potrebuje zmenu?

Začnime tým, ako dnes pracujete. Technológiu vyberieme podľa toho.

Prebrať váš projekt