====== Persistència en dispositius Android ======
La persistència en Android és un tema sensible degut al model de seguretat que asseguri la privacitat de les dades de les aplicacions.
Referències:
* [[Android]]
* [[model_seguretat_android|Model de seguretat d'Android]]
{{tag> android #Dam #DamMp08 #DamMp08Uf01 #DamMp08Uf01 }}
===== Persistència =====
Segons [[https://developer.android.com/guide/topics/data/data-storage?hl=es-419|Android Data Storage]] tenim diversos tipus d'emmagatzematge:
- **Preferències compartides**: tipus simples en forma clau-valor.
- **Emmagatzematge Intern**: es guarda arxiu en la memòria interna del dispositiu, no es pot compartir entre aplicacions per seguretat.
* Els arxius es guarden a ''/data/data/com.myorg.myapp/files''.
- **Emmagatzematge "Extern" o Compartit**: SD card o memòria interna compartida entre aplicacions com la carpeta DCIM on hi ha el carret de fotos.
* Els arxius es guarden a ''/data/media/0/Android/data/com.myorg.myapp/files''.
- **BBDD SQLite**
- Connexió de xarxa (guardar en un servidor d'internet)
\\
===== Emmagatzematge intern =====
És el més segur ja que no es pot compartir entre aplicacions. La seguretat es manté sempre i quan no es faci //root// el dispositiu.
L'[[https://developer.android.com/reference/android/content/Context|objecte Context]] (del qual deriva ''Activity'') ens proveeix algunes funcions per tractar els fitxers:
* [[https://developer.android.com/reference/android/content/Context#openFileOutput(java.lang.String,%20int)|Context.openFileOutput()]] ens permet obrir un //stream// d'escriptura a l'espai privat de l'aplicació.
* [[https://developer.android.com/reference/android/content/Context#getFilesDir()|Context.getFilesDir()]] ens dona el ''path'' de la carpeta privada de l'aplicació on emmagatzemarem els fitxer interns. Amb aquest ''File'' com a path podem utilitzar les funcions habituals de Java per tractament d'arxius.
==== Exercici ====
**Mini agenda**
App de testeig per visualitzar els arxius i les carpetes d'emmagatzematge intern.
* Crea una nova aplicació amb una interfície que disposi dels camps:
* Nom
* Cognoms
* Telèfon
* Email
* Botó GUARDAR
* Quan premis el botó GUARDAR s'enregistrarà la informació de la interfície en un arxiu de memòria interna.
* Utilitza la funció [[https://developer.android.com/reference/android/content/Context#openFileOutput(java.lang.String,%20int)|Context.openFileOutput()]] per enregistrar un arxiu amb el nom "contactes.txt".
* Controla que tingui el seu ''try...catch'' i mostra un missatge d'error per si falla alguna cosa.
* Escriu-hi una línia amb les dades del contacte separats per punts i coma ";".
* Utilitza el [[https://developer.android.com/studio/debug/device-file-explorer|Device File Explorer]] d'Android Studio per visualitzar l'arxiu que has generat. Pensa a ACTUALTIZAR el File Manager per trobar el teu arxiu.
Ampliació (només si dona temps):
* Fes una segona ''Activity'' que disposi d'un ''ListView'' que carregui els contactes en un ''ArrayList'' i els visualitzi. Consulta [[Android ListView]].
* Posa un event d'acció a la ''ListView'' per tal que, al clicar un element de la llista, es pugui editar en una ''Activity'' a part.
\\
===== Emmagatzematge compartit =====
Si utilitzem aquest espai, guardarem l'arxiu en una carpeta compartida on les diferents aplicacions poden intercanviar informació, com en el carret de fotos (DCIM) o la carpeta de descàrregues (Downloads).
Dins de l'emmagatzematge compartit hi ha 2 zones:
* **Zona de l'aplicació**
* Ubicat a ''/media/0/Android/data/com.myorg/myapp''.
* Dins d'aquesta hi ha també un seguit de carpetes específiques per a Pictures, Music, etc.
* Podem accedir a l'arrel i crear les carpetes que creiem necessàries.
* No requereix permisos d'accés.
* **Zona compartida de ''MediaStore''**
* Ubicat a ''/media/0/''.
* Hi ha carpetes específiques (fixes) com DCIM, Documents, Downloads, etc.
* S'hi accedeix mitjançant l'objecte [[https://developer.android.com/training/data-storage/shared/media|MediaStore]] però la seva aproximació és molt diferent de la que estem treballant aquí ara.
* Requereix permisos d'accés READ_EXTERNAL_STORAGE i/o WRITE_EXTERNAL_STORAGE.
En cas d'utilitzar emmagatzematge compartit caldrà declarar al ''manifest.xml'' els permisos pertinents:
...
Des de la API 18 en endavant (Android 4.4 KitKat) ja no cal demanar els permisos per escriure en l'àrea externa de la pròpia //app//, per això apareix aquest ''maxSdkVersion=18'' en els permisos. Només cal tenir en compte que les altres aplicacions també podran accedir a aquestes dades si ho requereixen.
Si volem accedir a les imatges i arxius d'altres //apps// mitjançant ''MediaStore'' caldrà que traiem el ''maxSdkVersion'' i demanem els permisos READ_EXTERNAL_STORAGE i WRITE_EXTERNAL_STORAGE.
Als objectes [[https://developer.android.com/reference/android/content/Context|Context]] i [[https://developer.android.com/reference/android/os/Environment|Environment]] trobem funcions adequades per a accedir als arxius externs de l'app:
* [[https://developer.android.com/reference/android/content/Context#getExternalFilesDir(java.lang.String)|Context.getExternalFilesDir(String type)]] ens dona el ''path'' de l'espai compartit dedicat a la nostra aplicació.
* [[https://developer.android.com/reference/android/os/Environment#getExternalStoragePublicDirectory(java.lang.String)|Environment.getExternalStoragePublicDirectory(String type)]] ens dona el ''path'' de l'espai compartit comú de tots les aplicacions.
[[https://gist.github.com/granoeste/5574148|Aquí teniu una taula resum dels espais d'emmagatzematge]].
A totes dues funcions cal especificar-les a quina de les diferents carpetes volem emmagatzemar-ho. Els valors s'indiquen a l'objecte [[https://developer.android.com/reference/android/os/Environment#fields|Environment]] i alguns exemples son: Environment.DIRECTORY_DCIM, Environment.DIRECTORY_MUSIC, Environment.DIRECTORY_SCREENSHOTS, etc.
\\
===== Treballant amb XML i altres =====
És pràctic utilitzar arxius XML per separar els camps de forma llegible. Hi ha diverses llibreries per llegir XML amb Java:
- [[https://stackoverflow.com/questions/11687074/create-xml-file-and-save-it-in-internal-storage-android|XmlSerializer]] és una opció.
- Aquest article en castellà explica força bé com [[http://jmoral.es/blog/xml-dom|utilitzar DOM parser]].
- Potser et pot aclarir tb aquest [[https://stackoverflow.com/questions/773012/getting-xml-node-text-value-with-java-dom|post de DOM parser]]
- Algun [[https://stackoverflow.com/questions/5386991/java-most-efficient-method-to-iterate-over-all-elements-in-a-org-w3c-dom-docume|exemple mes de DOM parser]].
\\
===== Exercicis =====
Continuació de l'aplicacio "Andrevina" (endevinar el número del 1 al 100) iniciada a l'article [[Android]].
Continuant amb l'aplicació "Andrevina" i la taula de rècords:
- Després d'entrar el nom per enregistrar un rècord, a més, engegarem la càmera.
- Capturem la imatge i la enregistrem a disc amb un nom aleatori o seqüencial.
- Assignem la imatge al nou item de rècord.
- Modifiquem el //layout// del ''ArrayAdapter'' per afegir-hi una ''ImageView'' al rècord (on havíem de tenir prèviament el nom i els intents).
- Modifiquem adequadament el //adapter// per aconseguir que ens agafi la imatge de disc i la assigni a la ''ListView''
\\
En el cas que no estiguis desenvolupant l'app "Andrevina" pots fer aquest altre exercici:
* Implementa una ''ListView'' tal i com s'explica a l'article [[Android ListView]].
* Modifica el ''layout'' per tal de que cada ''list_item'' tingui també un espai per a la imatge.
* Afegeix un botó per capturar imatges i enregistrar-les amb diferents noms. Cada cop que enregistris una d'elles, assigna-la al primer element de la llista que no en tingui (al //Model// de la llista, o sigui, el ''ArrayList'').
* Actualitza la //view// de manera que es visualitzin els canvis.