====== 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.