Podporuje Android SD kartu? blog

Podporuje Android SD kartu?

Účelom článku je poukázať na problémy pri vývoji Android mobilných aplikácií pri práci s SD kartou. Blog vychádza z reálnych skúseností na komerčnom projekte. Článok neponúka úplné riešenia na celú problematiku SD karty, kompletné riešenie spadá pod naše know-how.

Zadanie od klienta bolo veľmi jednoduché. Spraviť aplikáciu, ktorá bude pre platformu Android podporovať SD kartu pre rozdielne druhy zariadení. Zo zjavne jednoduchého zadania vznikol veľmi obsiahly projekt, ktorý sa snaží vyriešiť čítanie a zápis na SD kartu pre rozdielne Android zariadenia. Blog spisuje zoznam problémov, na ktoré môžete naraziť počas práce s SD kartou.

Blog nemá slúžiť ako hate na platformu Android, ale skorej poukázanie na nejednotnosť a nedodržiavanie Android špecifikácie zo strany výrobcov telefónov, ktorí jednotlivé Android zariadenia modifikujú do takej miery, že strácajú funkčnosť, ktorú poskytuje samotný čistý Android.

Blog sa opiera o reálny testing na produkčnom projekte na veľkom množstve Android zariadení. Testovanie prebiehalo na zariadeniach určených pre Európske a Ázijské krajiny. V testovacej sade zariadení sa nachádzajú aj zariadenia, ktoré sú čisto špecifické len pre niektoré krajiny a mimo týchto krajín sú zariadenia nepredajné.

Android a nájdenie cesty k SD karte

Android zariadenia nemajú SD kartu pripojenú vždy na tej istej ceste. To kde sa pripája SD karta, je rôzne pri rozdielnych výrobcoch a pre jednotlivé druhy telefónov. Sú zariadenia, ktoré prípájajú SD kartu na rozdielne cesty, podľa toho aký druh SD karty sa tam vloží (zariadenie sa rozhoduje o ceste dynamicky podľa interného čísla SD karty).

Keďže cesta sa pre jednotlivé zariadenia generuje dynamicky, tak sa nedá fixne do aplikácie zadefinovať cesta na SD kartu. Treba ju získavať programovo. Android ponúka pre nájdenie cesty k SD karte metódu getExternalStorageDirectory()

Environment.getExternalStorageDirectory().getAbsolutePath()
Android JavaDoc getExternalStorageDirectory(): Return the primary shared/external storage directory. This directory may not currently be accessible if it has been mounted by the user on their computer, has been removed from the device, or some other problem has happened. You can determine its current state with getExternalStorageState().
Note: don't be confused by the word "external" here. This directory can better be thought as media/shared storage. It is a filesystem that can hold a relatively large amount of data and that is shared across all applications (does not enforce permissions). Traditionally this is an SD card, but it may also be implemented as built-in storage in a device that is distinct from the protected internal storage and can be mounted as a filesystem on a computer.

Po testovaní na rozdielnych zariadeniach zistíte, že na túto metódu sa nedá spoľahnúť a treba hľadať "inú cestu".

Pomerne spoľahlivé riešenie je získavanie cesty k Android SD karte nasledovne

private static final String SECONDARY_STORAGE = "SECONDARY_STORAGE";
final String value = System.getenv(SECONDARY_STORAGE);

Bohužiaľ ani druhé riešenie, aj keď má lepšie výsledky na reálnom Android testovaní je stále nespoľahlivé a nie je reálne použiteľné pre všetky Android zariadenia. V dokumentácií sa bohužiaľ nedočítate o alternatívach, ako pokračovať ďalej. Pracovanie s SD kartou zlyháva na úplnom začiatku a to nájdení cesty na SD kartu. Iné spôsoby hľadania cesty a celkové spoľahlivé riešenie na tento problém sú nad rámec tohoto blogu.

Android a nežiadané odpájanie SD karty

Niektoré Android zariadenia majú v sebe systém, ktorý automaticky "odpája" SD kartu, pokým je telefón v nečinnosti. Toto správanie zvyšuje životnosť výdrže batérie. Pre vývoj mobilnej aplikácie, ktorá má pracovať s SD kartou to je však nežiadaný stav. Ak sa aplikácia spolieha na SD kartu, tak musí mať mechanizmus, ktorý vyčkáva, kým zariadenie stihne dať kartu zo "sleep" módu do pracovného módu a až potom s ňou môže reálne pracovať. Android zariadenie nevykonáva reálny unout karty, skorej dochádza k čiastočnému obmedzeniu napájania pre SD kartu.

Pri tvorbe aplikácie treba zapracovať mechanizmus, ktorý overí, či karta už reálne nabehla, až potom sa s kartou môže pracovať. Toto správanie nie je v Android dokumentácii nikde spomenuté, je to skorej špecifické "vylepšenie" zo strany výrobcov Android telefónov. S týmto správaním sa môžete stretnúť pri HTC zariadeniach, napríklad HTC J One HTL22.

Obmedzenie práv zápisu na SD kartu

Android verzia KitKat (4.4) a Lollipop (5.0) zakazuje zapisovať aplikáciám na SD kartu mimo svojho vyhradeného adresára. Žiadna aplikácia tretej strany (nie natívne aplikácie), nemôžu zapisaovať na SD kartu inde, ako to povoľuje Android. Toto obmedzenie spôsobilo nefunkčnosť už aj existujúcim aplikáciám nainštalovaných v Android zariadení po prechode na Kitkat alebo Lolipop. Skúste si napríklad nainštalovať do Android zariadenia TotalCommander a zeditujte súbor na SD karte, mimo KitKat a Lolipop uspejete (nie rootnutý telefón).

Dnešné aplikácie to obchádzajú tak, že z externých adresárov si kopírujú súbory (pretože na čítanie majú právo aj mimo svoj adresár) do svojho adresára a tam ho následne vedia editovať a uložiť. Takto dochádza k duplikovaniu dát na SD karte.

Sú riešenia na fórach, ktoré vedia obmedzenie vypnúť. Programátor vyvíja produkt a nemôže každému zákazníkovi ručne hackovať telefón, preto root telefónu, alebo mazanie systémových súborov, nie je to ,čo pri tvorbe aplikácií budete preferovať.

Performance SD karty a cachovanie zo strany Androidu

Práca so súbormi s SD karty je pomalšia ako práca so súbormi, ktoré sa nachádzajú v internej pamäti telefónu. Nie je to spôsobené len rýchlosťou samotných SD kariet, ale najmä komunikačnou vrstvou, ktorá je medzi SD kartou a Android zariadením. Android na vyriešenie tohoto problému používa systém cachovania súborov. Opätovné otvorenie toho istého súboru sa nevykonáva už z SD karty, ale z pamäti telefónu, kde sa súbor ukladá do medzi pamäte. Veľkosť cache pamäte je rôzna pre rozdielne Android zariadenia, ale môže to byť až niekoľko GB.

Ukážka cachovania súborov na SD karte

Aplikácia načítava súbory z SD karty a loguje čas načítavania.

Prvé načítanie 5 súborov a výpis času načítavania súborov. Prvé načítanie súboru sa vykonáva z SD karty.

(FINE)sk.starbug.sdcardtest.PerformanceTest log: Read file: testfile1.bin
                        (FINE)sk.starbug.sdcardtest.PerformanceTest log: File read time: 230 ms
                        (FINE)sk.starbug.sdcardtest.PerformanceTest log: Read file: testfile2.bin
                        (FINE)sk.starbug.sdcardtest.PerformanceTest log: File read time: 201 ms
                        (FINE)sk.starbug.sdcardtest.PerformanceTest log: Read file: testfile3.bin
                        (FINE)sk.starbug.sdcardtest.PerformanceTest log: File read time: 245 ms
                        (FINE)sk.starbug.sdcardtest.PerformanceTest log: Read file: testfile4.bin
                        (FINE)sk.starbug.sdcardtest.PerformanceTest log: File read time: 212 ms
                        (FINE)sk.starbug.sdcardtest.PerformanceTest log: Read file: testfile5.bin
                        (FINE)sk.starbug.sdcardtest.PerformanceTest log: File read time: 228 ms
                        (FINE)sk.starbug.sdcardtest.PerformanceTest log: Total read time: 1116 ms

Druhé načítanie 5 súborov. Android interne nacachoval súbory do medzi pamäte a reálne už nečíta súbory z SD karty.

(FINE)sk.starbug.sdcardtest.PerformanceTest log: Read file: testfile1.bin
                               (FINE)sk.starbug.sdcardtest.PerformanceTest log: File read time: 12 ms
                               (FINE)sk.starbug.sdcardtest.PerformanceTest log: Read file: testfile2.bin
                               (FINE)sk.starbug.sdcardtest.PerformanceTest log: File read time: 16 ms
                               (FINE)sk.starbug.sdcardtest.PerformanceTest log: Read file: testfile3.bin
                               (FINE)sk.starbug.sdcardtest.PerformanceTest log: File read time: 11 ms
                               (FINE)sk.starbug.sdcardtest.PerformanceTest log: Read file: testfile4.bin
                               (FINE)sk.starbug.sdcardtest.PerformanceTest log: File read time: 13 ms
                               (FINE)sk.starbug.sdcardtest.PerformanceTest log: Read file: testfile5.bin
                               (FINE)sk.starbug.sdcardtest.PerformanceTest log: File read time: 15 ms
                               (FINE)sk.starbug.sdcardtest.PerformanceTest log: Total read time: 67 ms

Porovnanie časov

Čítanie z SD karty Opätovné čítanie súborov z SD karty
Android uplatňuje svoj cache mechanizmus
Čas načítavania 1116 ms Čas načítavania 67 ms

Ušetrený čas: 1049 ms

Android síce vyriešil problém rýchlosť čítania súborov z SD karty, ale pri programovaní Android aplikácie môžete natrafiť na problém, že Vám Android bude vracať stále starý a nie nový súbor. Na tento problém narazíte napríklad, keď súbor modifikuje externý zdroj mimo Android zariadenia, čiže Android nemá ako vedieť, že sa súbor zmenil a preto bude stále vracať starý (už neaktuálny) nacachovaný súbor.

Neimplementovanie O_DIRECT v Android jadre

Tento bod súvisí s cachovaním súborov v Android zariadení. Na obídenie cachovania Android v sebe implementuje C-čkový parameter O_DIRECT. Ak sa súbor načítava mimo javu a použije sa C-čkové volanie, tak sa dá cache obísť nasledovne

Otvorenie súboru v Android zariadení cez C-čkové volanie JNI - Java Native Interface

open(mfile, O_RDONLY | O_DIRECT | O_SYNC );

Bohužiaľ na Android KitKat O_DIRECT nie je zimplementovaný, čo zabraňuje načítavať súbor mimo cache Android zariadenia. Programátor reálne nemá možnosť sa dostať k "čerstvému" súboru na SD karte, lebo Android ho dáva do cache a priame načítanie cez O_DIRECT nefunguje. Viac o bugu sa dozviete na tomto fóre

Nevytvorenie Android adresára po inštalácii programu

Každá aplikácia v Androide má svoj špecifický adresár, do ktorého môže zapisovať a s ktorým vie korektne pracovať. Staršie Androidy podporovali zápis aj mimo svojho vyhradeného adresára, ale to už nie je možné (viď "2. Obmedzenie práv zápisu na SD kartu"). Predefinovaný adresár, do ktorého vie Android aplikácia zapisovať je

<cesta k SD karte>/Android/data/moj.nazov.balika.aplikacie/

Android zariadenia štandardne majú vytvárať adresár po inštalácií aplikácie na SD karte. Sú však prípady, kedy sa tento adresár nevytvorí a preto treba zabezpečiť, aby tento adresár vytvoril samotný program. Štandardný spôsob vytvárania adresáru cez Javu

// -----------------------------------
// create directory with native directory create
// -----------------------------------
try {
	File appDir = new File(/*TODO cesta k súboru, ktorý sa má zapísať na SD kartu*/);
	appDir.mkdir();
	if (appDir.exists()) {
    		LOG.info("Directory created.");
    	} else {
    		LOG.info("Creation fialed!");
    	}
    } catch (Exception e) {
    	LOG.info("Failed to create directory with native mkdir(). Reason: " + e.getMessage());
    }

Tento spôsob je síce programátorsky správny, ale vytvorenie adresáru nezafunguje pre všetky zariadenia. Druhý spôsob, o ktorom sa už nedozviete v dokumentácii je použitie context.getExternalFilesDir(null). Android pri tomto volaní vyhodnotí adresár ako hlavný adresár pre aplikáciu a vytvorí správnu štruktúru pre program automaticky. Vytvorenie je tak, ako keby sa vykonala inštalácia aplikácie na zariadení, ktoré nemá problém s vytvorením adresára.

// -----------------------------------
// create directory with another native way
// -----------------------------------
try {
	File dir = context.getExternalFilesDir(null);
	dir.mkdir();
	if (appDir.exists()) {
		LOG.info("Directory created.");
	} else {
		LOG.info("Creation fialed!");
	}
} catch (Exception e) {
	LOG.info("Failed to create directory with native getExternalFilesDir(null). Reason: " + e.getMessage());
}

Problém nastáva s Android KitKat a Lillipop. Obmedzenie zápisu na SD kartu je striktne vyhradené len pre adresár aplikácie. Ako sa má vytvoriť adresár pre Android aplikáciu, keď ju nevytvorí natívne Android a KitKat a Lolipop? KitKat a Lollipop nepovoľujú zápis mimo adresáru aplikácie, čiže reálne Android nepovolí vytvoriť ani adresár, kotrý je určený pre zápis súborov aplikácie (ak to Android nevytvoril automaticky). To ako sa to dá opraviť už neprezradíme ;)

Zhrnutie

Podporuje Android vážne SD kartu? Áno podporuje, ale práca s ňou pre vývojárov je neuveriteľne zložitá. Vytvorená aplikácia sa správa nejednotne pre rozdielne druhy Android telefónov. Vývoj aplikácie sa nedá odhaliť na simulátore, ale treba mať reálne zariadenia. Pre korektné naprogramovanie zápisu na SD kartu, treba mať kvantum telefónov od rozdielnych výrobcov s rozdielnymi typmi a verziami Android OS.

Zo zjavne triviálneho problému, ako je zápis a práca s SD kartou sa stáva zložité riešenie, ktoré vzniká kvôli modifikáciám výrobcov telefónov a chybami, ktoré sa dostali do Android jadra. Samotný Google robí updaty na Android OS pravidelne, ale update sa len málokedy dostane na všetky Android zariadenia, práve kvôli modifikáciám samotných výrobcov. Vývojár je nútený robiť aplikáciu aj pre staré sytémy, aj napriek tomu, že veľa problémov už môže byť vyriešených v novších verziách Android OS.