Diferencia entre revisiones de «PDM Avanzado Datos Persistentes Arquivos»

De MediaWiki
Ir a la navegación Ir a la búsqueda
(A clase File e a clase Environment)
(A clase File e a clase Environment)
 
(No se muestran 2 ediciones intermedias del mismo usuario)
Línea 79: Línea 79:
  
 
* Así se quero:
 
* Así se quero:
:* Gardar información privada dentro do almacenamento externo terei que chamar ao [https://developer.android.com/reference/android/content/Context?hl=es-419#getExternalFilesDir(java.lang.String) método getExternalFilesDir()]. Este método <u>non pertence á clase Environment, se non que é da Activity</u>. Este método leva un argumento no que podemos especificar un cartafol determinado, por exemplo [https://developer.android.com/reference/android/os/Environment?hl=es-419#DIRECTORY_MOVIES  Environment.DIRECTORY_MOVIES]  ou ben podemos crear nos un a partires do cartafol ráiz da forma: Environment.getExternalFilesDir(null).
+
:* Gardar información privada dentro do almacenamento externo terei que chamar ao [https://developer.android.com/reference/android/content/Context?hl=es-419#getExternalFilesDir(java.lang.String) método getExternalFilesDir()]. Este método <u>non pertence á clase Environment, se non que é da Activity</u>. Este método leva un argumento no que podemos especificar un cartafol determinado, por exemplo [https://developer.android.com/reference/android/os/Environment?hl=es-419#DIRECTORY_MOVIES  Environment.DIRECTORY_MOVIES]  ou ben podemos crear nos un a partires do cartafol ráiz da forma: getExternalFilesDir(null).
 
:: A partires da versión Android 4.4 se <u>escribimos nestes cartafoles 'privados'</u> dentro do almacenamento externo, non fai falla pedir permiso de escritura no AndroidManifiest.xml. Por tanto podemos indicar que dito permiso só sexa necesario se es inferior ou igual á API 18:
 
:: A partires da versión Android 4.4 se <u>escribimos nestes cartafoles 'privados'</u> dentro do almacenamento externo, non fai falla pedir permiso de escritura no AndroidManifiest.xml. Por tanto podemos indicar que dito permiso só sexa necesario se es inferior ou igual á API 18:
 
::<syntaxhighlight lang="java" line enclose="div" highlight="3">
 
::<syntaxhighlight lang="java" line enclose="div" highlight="3">
Línea 95: Línea 95:
  
  
:* Gardar información no memoria interna, teremos que chamar ao método getFilesDir() <u>(tamén pertence á Activity, non é da clase Environment)</u>. Esta información será borrada cando a aplicación se desinstale.
+
:* Gardar información no memoria interna, teremos que chamar ao método getFilesDir() <u>(tamén pertence á Activity, non é da clase Environment)</u>.
 +
:: Ao facelo, obteremos como ruta /data/data/nome_do_paquete/files.
 +
:: A información gardada neste cartafol será borrada cando a aplicación se desinstale.
  
  

Revisión actual del 21:50 7 nov 2019

Introdución

  • O tratamento dos ficheiros en Android é idéntico a Java.


Introdución á E/S en Java

  • O paquete java.io contén as clases para manipular a E/S.
  • En java a entrada/saída xestionase a través de streams (fluxos) , e estes poden interactuar cun teclado, a consola, un porto, un ficheiro, outro stream, etc.
  • Todo stream ten un orixe e un destino.


  • O fluxo máis básico de E/S son os fluxos de bytes, pero un fluxo de bytes pode ser a entrada doutro fluxo máis complexo, até chegar a ter fluxos de caracteres, e de buffers e o mesmo á inversa.
  • Para aquel usuario que desexe repasar ou afondar na E/S en java déixanse os seguintes enlaces:
    • Curso de Java nos Manuais do IES San Clemente: Entrada/Saída
    • Diagramas moi gráficos (valga a redundancia) das xerarquías de clases de E/S, onde se poden ver as clases, atributos, construtores, métodos, etc dun modo moi claro:
    • http://www.falkhausen.de/en/diagram/html/java.io.Writer.html (Neste caso da xerarquía writer).
    • A modo de exemplo amósase un exemplo de diagrama da xerarquía Reader:
    • Observar no diagrama como os construtores da clase InputStreamReader reciben como parámetro outro stream/fluxo de tipo InputStream (Que está noutra xerarquía).

Java-io-class-hierarchy-diagram.gif

Introdución aos ficheiros en Android

  • Os ficheiros en Android poden servirnos para almacenar información de modo temporal, para pasar información entre dispositivos, para ter unha "mini" base de datos, etc.
  • En Android os ficheiros poden almacenarse en tres sitios (e dentro dun deles en 2 directorios distintos).
    • Na propia aplicación:
  • A modo de recurso (como cando incluímos unha imaxe): /res/raw/ficheiro... (raw significa cru). Neste caso referenciamos o recurso a través da clase R.
  • No cartafol /assets/. A diferenza do anterior non se xera ningún identificador en R e debemos referenciar os recursos gardados neste cartafol a través da clase AssetManager.
Neste lugar podemos crear unha estrutura (con cartafoles) cousa que en /res/raw/ non podemos facer.
Podemos facer operacións de só de lectura.
    • Na memoria interna, no subdirectorio files da carpeta da aplicación: /data/data/paquete_java/files/ficheiro....
Podemos facer operacións de lectura / escritura.
    • Na tarxeta SD, se existe, en 2 posibles subdirectorios:
      • /storage/sdcard/directorio que indique o programador, se indica/ficheiro...
      • /storage/sdcard/Android/data/paquete_java/files/ficheiro... (Algo parecido á memoria interna). Deste xeito se se desinstala a aplicación, tamén se borraría o ficheiro automaticamente, cousa que non pasaría no caso anterior
Podemos facer operacións de lectura / escritura.


Primeiro debemos decidir onde gardaremos os datos. Na memoria interna ou na tarxeta externa. Cada opción ten as súas vantaxes e desvantaxes:

  • Interna:
  • Está sempre dispoñible.
  • Por defecto os datos gardados están só dispoñibles para a aplicación.
  • Cando o usuario desinstala a aplicación os datos son borrados.
  • Externa:
  • Pode non estar dispoñible (o usuario pode quitar a tarxeta ou non tela).
  • Poden acceder os datos fora da nosa aplicación.
  • Os datos gardados aquí permanecen. Unicamente se borran se os gardamos no cartafol indicado polo método getExternalFilesDir().

A clase File e a clase Environment

Veremos máis adiante que para referenciar a arquivos que se atopan na memoria interna ou sd externa imos facer uso da clase File.

Un obxecto da clase File vai poder 'apuntar' a un cartafol / arquivo que se atope na memoria interna / sd externa do dispositivo.

Para apuntar necesitaremos enviarlle como parámetro a ruta a dito arquivo / cartafol desta forma:

1 File arquivo = new File("/sdcard/pictures/imaxe.jpg");

No exemplo estariamos apuntado a unha imaxe que se atopa na tarxeta sd externa no cartafol /pictures.


Para indicar as rutas, en vez de escribilas directamente, como fixemos no exemplo, faremos uso da clase Environment.

Dita clase vainos devolver rutas a sitios predeterminados do noso dispositivo Android.

Por exemplo:

  • Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES): Cartafol recomendado para gardar fotos (dará como resultado /sdcard/pictures/ ou o seu equivalente).
  • Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES): Cartafol recomendado para gardar vídeos.
  • Environment.getExternalStorageDirectory(): Cartafol do almacenamento externo en Android. Indicar que esta función non ten por que devolver unha ruta á sdcard externa. Debemos pensar que é un almacenamento no que existen uns cartafoles 'compartidos' por todas as aplicacións (Pictures, Videos, Music,...) e que ademáis se poden crear cartafoles 'privados' para cada aplicación dentro deste almacenamento o cal pode crearse a partires dun almacenamento interno particionado ou ben dunha sd externa como se indica no seguinte enlace.
  • Así se quero:
  • Gardar información privada dentro do almacenamento externo terei que chamar ao método getExternalFilesDir(). Este método non pertence á clase Environment, se non que é da Activity. Este método leva un argumento no que podemos especificar un cartafol determinado, por exemplo Environment.DIRECTORY_MOVIES ou ben podemos crear nos un a partires do cartafol ráiz da forma: getExternalFilesDir(null).
A partires da versión Android 4.4 se escribimos nestes cartafoles 'privados' dentro do almacenamento externo, non fai falla pedir permiso de escritura no AndroidManifiest.xml. Por tanto podemos indicar que dito permiso só sexa necesario se es inferior ou igual á API 18:
1 <manifest ...>
2     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
3                      android:maxSdkVersion="18" />
4     ...
5 </manifest>
Indicar que
  • Se desinstalamos a aplicación, o contido destes cartafoles se borran, polo tanto non vale para gardar información que queremos que se manteña na tarxeta.
  • Aínda que indiquemos que a información é privada (as fotos por exemplo non aparecerán na Gallery do S.O) , ao gardala neste tipo de almacenamento, se o sistema ten unha aplicación con permiso de lectura READ_EXTERNAL_STORAGE poderá acceder a dita información.
  • Gardar información que sexa permanente e non se borre cando se desinstala a aplicación teremos que facer uso do método getExternalStoragePublicDirectory() indicando un tipo de directorio ou creando nos un novo, como no caso de getExternalFilesDir().


  • Gardar información no memoria interna, teremos que chamar ao método getFilesDir() (tamén pertence á Activity, non é da clase Environment).
Ao facelo, obteremos como ruta /data/data/nome_do_paquete/files.
A información gardada neste cartafol será borrada cando a aplicación se desinstale.


As chamadas anteriores devolven un obxecto da clase File.

Se queremos obter a ruta en forma de cadea debemos chamar ao método getAbsolutePath() da forma seguinte.

Por exemplo:

1 Log.i("ARQUIVO",Environment.getDataDirectory().getAbsolutePath());
2 Log.i("ARQUIVO",Environment.getExternalStorageDirectory().getAbsolutePath());
3 Log.i("ARQUIVO",Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getAbsolutePath());
4 Log.i("ARQUIVO",Environment.getRootDirectory().getAbsolutePath());

Dará como resultado:

PDM Avanzada DatosPersistentes 14B.jpg
No emulador:
PDM Avanzada DatosPersistentes 14B2.jpg


Existen outros métodos relacionados coa clase Environment como por exemplo:

  • Environmet.getExternalStorageState() devolve un valor que nos sirve para saber se a tarxeta sd externa está dispoñible en modo lectura/escritura:
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
	Log.e("ARQUIVO","TARXETA NON MONTADA");
}
  • Environment.isExternalStorageEmulated() indica se a tarxeta sd é emulada:
if (Environment.isExternalStorageEmulated()){
        Log.i("ARQUIVO","TARXETA EMULADA");
}


A partires dun obxecto da clase File podemos facer as seguintes operacións (dependendo se apuntamos a un cartafol ou a un arquivo):

  • String getAbsolutePath(): devolve a ruta do cartafol / arquivo en forma de cadea.
  • String getName(): devolve o nome do arquivo ou cartafol do obxecto file.
  • Boolean IsDirectory(): indica se o obxecto file 'apunta' a un cartafol.
  • Boolean isFile(): indica se o obxecto file 'apunta' a un arquivo.
  • Long length(): devolve o tamaño en byte's do arquivo.
  • String[] list(): devolve un array de cadeas que representan a cada un dos arquivos que se atopan no cartafol indicado polo obxecto file.
  • File[] listFiles(): devolve un array de arquivos que representan a cada un dos arquivos que se atopan no cartafol indicado polo obxecto file.
  • Boolean mkdir(): crear un cartafol indicado polo obxecto File, asumindo que o cartafol pai existe.
Por exemplo: /storage/sdcard/arquivos/datos
Se fago o mkdir só vai crear o cartafol 'datos'.
  • Boolean mkdirs(): crea o cartafol indicado polo obxecto File e tamén todos os seus pais se son necesarios.
Por exemplo: /storage/sdcard/arquivos/datos
Se fago o mkdirs crearía o cartafol 'arquivos' e o cartafol 'datos'.
  • Boolean exists(): devolve true se a ruta ou arquivo existe.


Nota: Unha constante que tamén debe usarse é File.separator que representa o caracter de separación entre diferentes rutas (por exemplo en Linux é /).


Traballando con cartafoles

Para crear un novo cartafol teremos que engadir á nova ruta a ruta existente: Isto o podemos facer de dúas formas:

  • 1 File nova_ruta = new File(Environment.getExternalStorageDirectory(),"NOME_CARTAFOL_CREAR");
    2 nova_ruta.mkdirs();
    
Nesta forma non enviamos o separador (/).
  • 1 String novopath=Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator  + "NOME_CARTAFOL_CREAR");
    2 File nova_ruta = new File(novopath);
    3 nova_ruta.mkdirs();
    
Fixarse como nesta segunda forma temos que enviarlle o separador de arquivos (File.separator = "/").


Un exemplo para borrar un cartafol:

1 String borrarCartafol=Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator  + "NOME_CARTAFOL_BORRAR");
2 File borrar = new File(borrarCartafol);
3 borrar.delete();


Traballando con arquivos

O funcionamento é igual ao anterior, pero indicando o nome do arquivo a borrar / ler.

Por exemplo:

1 String novopath=Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator  + "ARQUIVOS");
2 File arquivo = new File(novopath,"arquivo_a_leer.txt");

Neste exemplo estamos a 'apuntar' a un arquivo de nome 'arquivo_a_leer.txt' se atopa na tarxeta SD Externa no cartafol ARQUIVOS.

Fixarse que isto non significa que dito arquivo exista. Podemos ter o obxecto da clase File para crear dito arquivo (o veremos despois).


Por exemplo, podemos comprobar si o arquivo existe:

1 String novopath=Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator  + "ARQUIVOS");
2 File arquivo = new File(novopath,"arquivo_a_leer.txt");
3 if (arquivo.exists()){
4    // PODEMOS LER
5 }

Unha vez temos a referencia ao obxecto File xa podemos ler / escribir.

Accedendo aos ficheiros

Neste punto imos aprender como ler datos dende os diferentes lugares de almacenamento descritos no punto anterior.
Existen outras moitas formas de facelo, nos imos explicar unha delas.

Podedes repasar as diferentes formas de acceder a un arquivo neste enlace da Wiki.


Dependendo se queremos ler textos ou bytes (por exemplo, no caso dunha imaxe), necesitamos obxectos pertencentes a clases diferentes:

Este obxecto vainos permitir percorrer todo o arquivo lendo datos nel.
  • Bytes (por exemplo unha imaxe, unha base de datos): O que necesitamos é obter un obxecto da clase InputStream no caso da lectura.

Como obtemos estes obxectos ?

Dependendo do lugar onde estea almacenada a información.


Accedendo a recursos internos (cartafol raw)

Neste caso o recurso é compilado xunto co resto de recursos e podemos acceder a eles coa clase R.raw.


Nota: Lembrar que en /res/raw/, ao igual que todo o que se atopa en /res/, o nome dos recursos só poden levar letras minúsculas e números así como guión baixo.

Binarios

Caso de imaxes ou bases de datos, por exemplo.

Neste caso podemos obter a referencia ao obxecto da clase InputStream da forma:

1 InputStream is_raw = getResources().openRawResource(R.raw.imaxe);

Sendo R.raw.imaxe unha imaxe gardada en /res/raw/ de nome "imaxe.jpg".

Texto

Caso de arquivos de texto como arquivos xml, txt, ou calquera outro que poida editarse cun editor de textos.

Neste caso podemos obter a referencia ao obxecto da clase InputStreamReader da forma:

1 InputStream is_raw = getResources().openRawResource(R.raw.arquivotexto);
2 InputStreamReader isreader_raw = new InputStreamReader(is_raw);

Sendo R.raw.arquivotexto un arquivo de texto gardado en /res/raw/ de nome "arquivotexto.txt".


Accedendo ao cartafol asset

Neste cartafol se gardan normalmente recursos que non necesitamos 'compilar' coa aplicación, coma poden ser gráficos, bases de datos,...

Permite crear unha estrutura de cartafoles como se fose un sistema de ficheiros (cousa que no cartafol raw non podemos facer).

Binarios
1 InputStream is_assets = getAssets().open("imagen.jpg");


Texto
1 InputStream is_assets = getAssets().open("texto.txt");
2 InputStreamReader isreader_assets = new InputStreamReader(is_assets);

Accedendo á memoria interna

Se queremos gardar algo na memoria interna do dispositivo teriamos que facer uso do seguinte método:

  • getFilesDir(): que devolve un obxecto da clase File, o cal 'apunta' a 'data/data/package_name/files'.

Lembrar que nesta memoria si podemos escribir datos.

Binarios
  • Lectura
1 		File ruta = getFilesDir();
2 		File arquivo = new File(ruta,"arquivo.txt");
3 		try {
4 			InputStream os_interna = new FileInputStream(arquivo);
5 		} catch (FileNotFoundException e) {
6 			// TODO Auto-generated catch block
7 			e.printStackTrace();
8 		}
  • Escritura
1 		File ruta = getFilesDir();
2 		File arquivo = new File(ruta,"arquivo.txt");
3 		try {
4 			OutputStream os_interna_out = new FileOutputStream(arquivo);
5 		} catch (FileNotFoundException e) {
6 			// TODO Auto-generated catch block
7 			e.printStackTrace();
8 		}
Texto
  • Lectura
Pode ser así:
1 		File ruta = getFilesDir();
2 		File arquivo = new File(ruta,"arquivo.txt");
3 		try {
4 		     FileInputStream fis_sd = new FileInputStream(arquivo);
5   		     InputStreamReader isreader_sd = new InputStreamReader(fis_sd);
6 		} catch (FileNotFoundException e) {
7 			// TODO Auto-generated catch block
8 			e.printStackTrace();
9 		}
Ou así:
1 		FileInputStream fis_sd=null;
2 		try {
3 			FileInputStream fis_sd = openFileInput(getFilesDir().getAbsolutePath() + File.separator + "arquivo.txt");
4                         InputStreamReader isreader_sd = new InputStreamReader(fis_sd);
5 		} catch (FileNotFoundException e) {
6 			// TODO Auto-generated catch block
7 			e.printStackTrace();
8 		}


  • Escritura

É igual ao anterior cambiando FileInputStream / InputStreamReader por FileOutputStream / OutputStreamWriter

Pode ser así:
1 		File ruta = getFilesDir();
2 		File arquivo = new File(ruta,"arquivo.txt");
3 		try {
4 		     FileOutputStream fos_sd = new FileOutputStream(arquivo);
5   		     OutputStreamWriter iswriter_sd = new OutputStreamWriter(fos_sd);
6 		} catch (FileNotFoundException e) {
7 			// TODO Auto-generated catch block
8 			e.printStackTrace();
9 		}
Ou así:
1 	try {
2 	    FileOutputStream fos_sd = openFileOutput(getFilesDir().getAbsolutePath() + File.separator + "arquivo.txt",MODE_PRIVATE);
3             OutputStreamWriter iswriter_sd = new OutputStreamWriter(fos_sd);
4 	} catch (FileNotFoundException e) {
5 		// TODO Auto-generated catch block
6 		e.printStackTrace();
7 	}


Engadir / Crear arquivo novo

No caso de escribir nun arquivo podemos abrilo para crealo ou para engadir novo contido.

Nos exemplos anteriores sempre sobreescribimos o arquivo.


Necesitaremos crear un obxecto da clase:

  • FileOutputStream(ficheiro) ou FileOutputStream(ficheiro,engadir), onde:
  • Ficheiro: é a ruta ao ficheiro que lle temos que indicar nós ou ben a lume (/data/data/.../files) ou facendo uso do método: getFilesDir(), que devolve a ruta ao directorio files da aplicación.
  • Engadir: booleano que indica se o ficheiro se abre en modo sobrescritura ou append.


Outra forma de obter un FileOutputStream é facendo uso do método openFileOutput(ficheiro, modo_acceso_ao_ficheiro).

Este método abre o ficheiro indicado no modo indicado, que pode ser:

  • MODE_PRIVATE: para acceso privado dende a nosa app, e non dende outras.
  • MODE_APPEND: para engadir datos a un ficheiro existente
  • MODE_WORLD_READABLE: permitir que outras app lean o ficheiro
  • MODE_WORLD_WRITEABLE: permitir que outras app lean/escriban o ficheiro
  • O método devolve un stream asociado ao ficheiro de tipo FileOutputStream, a partir de aquí xa podemos operar con ese fluxo como o faríamos en Java.
  • O método crea o ficheiro no cartafol: /data/data/paquete_java/files/ficheiro.

Accedendo á tarxeta SD Externa

  • Nalgunha versión de Android (probado nun API 27) a tarxeta SD Externa está montada en /mnt/media_rw/
Memoria Externa: permisos de lectura / escritura na tarxeta SD
  • Se imos ler na tarxeta SD:
  • Se a versión do S.O. Android é inferior á 4.1 non precisamos ningún permiso.
  • Se a versión do S.O. Android é superior ou igual á 4.1 debemos engadir o permiso: <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>.
  • Se imos escribir na tarxeta SD:
  • Se a versión do S.O. Android é inferior á 4.4 (API 19) o permiso é: <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> .
  • Se a versión do S.O. Android é a 4.4 ou superior API >= 19). Podemos poñer o mesmo permiso anterior pero as aplicacións dispoñen dun cartafol para escribir na SD (cartafol Android/data/paquete/) sen necesidade de ter o permiso anterior.


  • Importante::


  • Os permisos necesarios son postos no ficheiro AndroidManifest.xml da aplicación:

PDM Avanzada DatosPersistentes 14C.jpg


 1 <?xml version="1.0" encoding="utf-8"?>
 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android"  package="com.example.angel.olamundo" >
 3 
 4     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
 5 
 6     <application
 7         android:allowBackup="true"
 8         android:icon="@mipmap/ic_launcher"
 9         android:label="@string/app_name"
10         android:supportsRtl="true"
11         android:theme="@style/AppTheme" >
12         <activity android:name=".MainActivity" >
13             <intent-filter>
14                 <action android:name="android.intent.action.MAIN" />
15 
16                 <category android:name="android.intent.category.LAUNCHER" />
17             </intent-filter>
18         </activity>
19     </application>
20 
21 </manifest>
  • Importante: Débense poñer antes da etiqueta <application>.
Binarios

Faise igual que no caso da Memoria Interna, cambiando a ruta ao ficheiro.

Por exemplo (caso de lectura):

1 		File ruta = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC);
2 		File arquivo = new File(ruta,"musica.mp3");
3 		try {
4 			InputStream os_interna = new FileInputStream(arquivo);
5 		} catch (FileNotFoundException e) {
6 			// TODO Auto-generated catch block
7 			e.printStackTrace();
8 		}


Texto

Faise igual que no caso da Memoria Interna, cambiando a ruta ao ficheiro.

Por exemplo (caso de lectura):

1 		File ruta = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC);
2 		File arquivo = new File(ruta,"musica.mp3");
3 		try {
4 		     FileInputStream fis_sd = new FileInputStream(arquivo);
5   		     InputStreamReader isreader_sd = new InputStreamReader(fis_sd);
6 		} catch (FileNotFoundException e) {
7 			// TODO Auto-generated catch block
8 			e.printStackTrace();
9 		}

Lendo o contido dos arquivos

Unha vez que temos a referencia:

  • Á clase InputStreamReader para datos de texto.
  • Á clase InputStream para datos binarios.


Podemos ler o contido e traballar con el ou ben levalo a outro destino diferente (por exemplo, na sección das bases de datos imos copiar o base de datos (binario) dende o cartafol /assets/ ao cartafol /data/data/package_name/databases/)


A forma de ler / escribir é mediante un Buffer. Neste exemplo imos crear unha variable de tipo String e ir engadindo anacos de arquivo ata que rematemos de ler. Cada vez que lemos, gardamos nunha variable o número de bytes lidos, xa que na última lectura o número de bytes lidos pode ser menor que o tamaño do buffer.

O tipo de buffer dependerá do que vaiamos a ler. No caso de texto usaremos un buffer de tipo char, pero no caso de imaxes, bases de datos,...usaremos un de tipo byte.

InputStream / OutputStream <--------------------> byte
InputStreamReader / OutputStreamReader <--------------------> char


O proceso será o mesmo:

  • Definimos o tamaño do buffer en char / bytes e a variable para gardar os char / bytes lidos:
int tamRead;
char[] buffer = new char[2048];
  • Agora temos que ler. Usaremos o método read(buffer) da clase InputStreamReader (no caso de datos de tipo texto).
Dito método devolve o número de caracteres lidos (bytes) e o gardaremos en tamRead.
Isto o repetiremos ata que o método read devolva -1, co que chegaríamos ao final do ficheiro.
A chamada ao método read pode provocar unha excepción de entrada / saída (IOException) polo que o poñemos o código entre un try....catch(IOException ioe) ou lanzamos un throw IOException dende o procedemento onde estea definido.
		while((tamRead = isreader_raw.read(buffer))>0){

		}
  • Agora en cada iteración do bucle temos que engadir á variable String o lido.
Para isto usaremos o método da clase String:
copyValueOf(char[] buffer, int offset, int tamaño)
	while((tamread = isreader_raw.read(buffer))>0){
		cadea_lida += String.copyValueOf(buffer, 0, tamread);
	}


  • Por último, xa fora do bucle, pecharemos o InputStreamReader e o InputStream:
	isreader_raw.close();
	is_raw.close();


Nota: No caso de escribir necesitamos facer un flush, é dicir, asegurarnos que os últimos datos foron enviados a disco antes de pechar:

	osw_sdcard.flush();
	osw_sdcard.close();

Sendo osw_sdcard un obxecto da clase OutputStreamWriter.


O código completo dun exemplo de lectura de datos de tipo cadea que se atopan en /res/raw:

private void lerDatos() throws IOException{
	InputStream is_raw = getResources().openRawResource(R.raw.datos);
	InputStreamReader isreader_raw = new InputStreamReader(is_raw);

	int tamread;
	char[] buffer = new char[2048];
	String cadea_lida ="";
		
	while((tamread = isreader_raw.read(buffer))>0){
		cadea_lida += String.copyValueOf(buffer, 0, tamread);
	}
	isreader_raw.close();
	is_raw.close();
	Log.i(TAG_ARQUIVOS,cadea_lida);
}


Nota: Dependendo da ferramenta e o entorno onde credes o arquivo txt, pode ser necesario abrir o arquivo indicando o xogo de caracteres utilizado. Así:

InputStreamReader isreader_raw = new InputStreamReader(is_raw,"UTF-8");

Estariamos lendo un arquivo co xogo de caracteres UTF-8.

Aclaración: Como comentamos inicialmente o número de clases para facer operacións e E/S é moi amplo. No caso dos arquivos de texto pódese ler liña a liña utilizando outro tipo de Clases:

		InputStream is_raw = getResources().openRawResource(R.raw.proba);
		DataInputStream dis = new DataInputStream(is_raw);
		String lin = dis.readLine();

Escribindo contido nos arquivos

No proceso contrario, xa comentamos anteriormente que necesitamos un obxecto da clase OutputStream (datos binarios) ou OutputStreamWriter (datos de texto).

Xa sabemos conseguir dita referencia (dados nos puntos anteriores).

Unha vez o temos deberemos de chamar ao método write levando como parámetros os mesmos datos que no caso da lectura:

	osw.write (buffer, offset, tamread)

Sendo osw un obxecto da clase OutputStreamWriter.

Se o contido se obtén dun control (EditText) ou o temos localmente gardado (nunha variable) podemos gardar o seu contido desta outra forma:

String textoGardar="Exemplo de texto,.....";
osw.write(textoGardar + "\n");
..........
osw.flush();
osw.close();


Así. no exemplo anterior, se queremos copiar o contido do arquivo que se atopa en /res/raw a un cartafol da tarxeta sd externa de nome /DATOS/ (previamente creado) sendo o nome do arquivo de destino "datoscopiados.txt":

 1 	private void lerEscribir() throws IOException{
 2 		InputStream is_raw = getResources().openRawResource(R.raw.datos);
 3 		InputStreamReader isreader_raw = new InputStreamReader(is_raw);
 4 		
 5 		File fsaida = new File(Environment.getExternalStorageDirectory(),"DATOS"+File.separator+"datoscopiados.txt");
 6 		if (fsaida.exists()) fsaida.delete();
 7 		
 8 		FileOutputStream fos_sdcard = new FileOutputStream(fsaida);
 9 		OutputStreamWriter osw_sdcard = new OutputStreamWriter(fos_sdcard);
10 		
11 		int tamread;
12 		char[] buffer = new char[2048];
13 		String cadea_lida ="";
14 		
15 		while((tamread = isreader_raw.read(buffer))>0){
16 			osw_sdcard.write(buffer, 0, tamread);
17 			cadea_lida += String.copyValueOf(buffer, 0, tamread);
18 		}
19 		isreader_raw.close();
20 		is_raw.close();
21 		osw_sdcard.flush();
22 		osw_sdcard.close();
23 		Log.i(TAG_ARQUIVOS,cadea_lida);
24 		
25 	}

Caso Práctico

  • Nome do proxecto e da activity: UD5_03_DatosPersistentes_Arquivos

O obxectivo desta práctica e ver, gardar e recuperar información dende diferentes lugares físicos.

A práctica consistirá na seguinte Activity:

PDM Avanzada DatosPersistentes 15.jpg

Consta de:

  • EditText multilínea na parte superior onde se vai cargar o contido dun arquivo de texto.
  • Un botón 'Cargar Texto': Accede ao cartafol /res/raw/ e le o arquivo de texto subido previamente, pasando o seu contido ao EditText.
  • Un botón 'Gardar Texto': Garda o contido do EditText á tarxeta SD Externa, ao cartafol Download da mesma.
  • Un botón 'Copiar Imaxe': Copia unha imaxe (subida previamente) dende o cartafol /assets/ ao cartafol Download da tarxeta SD Externa.
  • Un botón 'Cargar Imaxe': Carga a imaxe copiada do botón anterior (a que se atopa na SD Externa) ao ImageView.
  • Un ImageView: Onde se visualiza a imaxe copiada da tarxeta SD Externa.



Preparación

Para crear o cartafol /assets/ o máis fácil e facelo dende a vista Proyect.

PDM Avanzada ficheros assets 1.jpg

  • Media:pdmavanzadotexto.txt: Gardar este arquivo no cartafol /res/raw/ do proxecto Android. Gardalo co seu nome en minúsculas.
  • Engade no AndroidManifiest.xml o permiso de escritura na SD Externa.

Creamos a Activity

  • Nome do proxecto: UD5_03_DatosPersistentes_Arquivos
  • Nome da activity: UD5_03_DatosPersistentes_Arquivos.java


Código do layout xml

 1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     xmlns:tools="http://schemas.android.com/tools"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5     tools:context="${relativePackage}.${activityClass}" >
 6 
 7     <EditText
 8         android:id="@+id/UD5_03_txtTexto"
 9         android:layout_width="match_parent"
10         android:layout_height="wrap_content"
11         android:layout_alignParentTop="true"
12         android:lines="3"
13         android:textSize="12sp"
14         android:inputType="textMultiLine"
15         android:layout_centerHorizontal="true"
16       >
17 
18         <requestFocus />
19     </EditText>
20 
21     <Button
22         android:id="@+id/UD5_03_btnCargarTexto"
23         android:layout_width="wrap_content"
24         android:layout_height="wrap_content"
25         android:layout_below="@+id/UD5_03_txtTexto"
26         android:layout_centerHorizontal="true"
27         android:text="CARGAR TEXTO" />
28 
29     <Button
30         android:id="@+id/UD5_03_btnGardarTexto"
31         android:layout_width="wrap_content"
32         android:layout_height="wrap_content"
33         android:layout_below="@+id/UD5_03_btnCargarTexto"
34         android:layout_centerHorizontal="true"
35         android:text="GARDAR TEXTO" />
36 
37     <ImageView
38         android:id="@+id/UD5_03_imgVwImaxe"
39         android:layout_width="100dp"
40         android:layout_height="100dp"
41         android:layout_alignParentBottom="true"
42         android:layout_centerHorizontal="true"
43         android:src="@drawable/ic_launcher" />
44 
45     <Button
46         android:id="@+id/UD5_03_btnCargarImaxe"
47         android:layout_width="wrap_content"
48         android:layout_height="wrap_content"
49         android:layout_above="@+id/UD5_03_imgVwImaxe"
50         android:layout_centerHorizontal="true"
51         android:text="CARGAR IMAXE" />
52 
53     <Button
54         android:id="@+id/UD5_03_btnCopiarImaxe"
55         android:layout_width="wrap_content"
56         android:layout_height="wrap_content"
57         android:layout_above="@+id/UD5_03_btnCargarImaxe"
58         android:layout_centerHorizontal="true"
59         android:text="COPIAR IMAXE" />
60     
61 </RelativeLayout>


Código da clase UD5_03_DatosPersistentes_Arquivos
Obxectivo: Ver, gardar e recuperar información dende diferentes lugares físicos

  1 import java.io.File;
  2 import java.io.FileInputStream;
  3 import java.io.FileNotFoundException;
  4 import java.io.FileOutputStream;
  5 import java.io.IOException;
  6 import java.io.InputStream;
  7 import java.io.InputStreamReader;
  8 import java.io.OutputStream;
  9 import java.io.OutputStreamWriter;
 10 import java.io.UnsupportedEncodingException;
 11 
 12 import android.app.Activity;
 13 import android.graphics.BitmapFactory;
 14 import android.os.Bundle;
 15 import android.os.Environment;
 16 import android.view.View;
 17 import android.view.View.OnClickListener;
 18 import android.widget.Button;
 19 import android.widget.EditText;
 20 import android.widget.ImageView;
 21 import android.widget.Toast;
 22 
 23 public class UD5_03_DatosPersistentes_Arquivos extends Activity {
 24 
 25 	private void xestionarEventos(){
 26 	
 27 		Button btnCargarArquivoTexto = (Button)findViewById(R.id.UD5_03_btnCargarTexto);
 28 		btnCargarArquivoTexto.setOnClickListener(new OnClickListener() {
 29 			
 30 			@Override
 31 			public void onClick(View v) {
 32 				// TODO Auto-generated method stub
 33 			
 34 				EditText edit = (EditText)findViewById(R.id.UD5_03_txtTexto);
 35 				
 36 			    InputStream is_raw = getResources().openRawResource(R.raw.pdmavanzadotexto);
 37 			    InputStreamReader isreader_raw;
 38 				try {
 39 					isreader_raw = new InputStreamReader(is_raw,"UTF-8");
 40 				} catch (UnsupportedEncodingException e1) {
 41 					// TODO Auto-generated catch block
 42 					e1.printStackTrace();
 43 					return;
 44 				}
 45 			    
 46 			    int tamread;
 47 			    char[] buffer = new char[2048];
 48 			    String cadea_lida ="";
 49 			               
 50 			    try {
 51 					while((tamread = isreader_raw.read(buffer))>0){
 52 			           cadea_lida += String.copyValueOf(buffer, 0, tamread);
 53 					}
 54 				    isreader_raw.close();
 55 				    is_raw.close();
 56 				} catch (IOException e) {
 57 					// TODO Auto-generated catch block
 58 					Toast.makeText(getApplicationContext(), "Houbo un erro na carga de datos:"+e.getMessage(), Toast.LENGTH_LONG).show();
 59 				}
 60 			    edit.setText(cadea_lida);
 61 				Toast.makeText(getApplicationContext(), "Texto cargado dende raw", Toast.LENGTH_LONG).show();
 62 			    
 63 			}
 64 		});
 65 
 66 		Button btnGardarArquivoTexto = (Button)findViewById(R.id.UD5_03_btnGardarTexto);
 67 		btnGardarArquivoTexto.setOnClickListener(new OnClickListener() {
 68 			
 69 			@Override
 70 			public void onClick(View v) {
 71 				// TODO Auto-generated method stub
 72 				File ruta = Environment
 73 						.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
 74 				File arquivo = new File(ruta, "datoscopia.txt");
 75 				EditText edit = (EditText) findViewById(R.id.UD5_03_txtTexto);
 76 
 77 				try {
 78 					FileOutputStream fos_sd = new FileOutputStream(arquivo,false);
 79 					OutputStreamWriter iswriter_sd = new OutputStreamWriter(fos_sd);
 80 					iswriter_sd.write(edit.getText().toString());
 81 					iswriter_sd.flush();
 82 					
 83 					iswriter_sd.close();
 84 					fos_sd.close();
 85 					
 86 					Toast.makeText(getApplicationContext(), "Texto gardado en " +arquivo.getAbsolutePath(), Toast.LENGTH_LONG).show();
 87 
 88 				} catch (FileNotFoundException e) {
 89 					// TODO Auto-generated catch block
 90 					e.printStackTrace();
 91 				} catch (IOException e) {
 92 					// TODO Auto-generated catch block
 93 					e.printStackTrace();
 94 				}
 95 
 96 			}
 97 		});
 98 		Button btnCopiarImaxe = (Button)findViewById(R.id.UD5_03_btnCopiarImaxe);
 99 		btnCopiarImaxe.setOnClickListener(new OnClickListener() {
100 			
101 			@Override
102 			public void onClick(View arg0) {
103 				// TODO Auto-generated method stub
104 				
105 				try {
106 					File ruta = Environment
107 							.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
108 					File arquivo_destino = new File(ruta, "badlogic_copia.jpg");
109 					OutputStream os = new FileOutputStream(arquivo_destino);
110 					InputStream is = getAssets().open("pdmavanzado_badlogic.jpg");
111 					int tamRead;
112 					byte[] buffer = new byte[2048];
113 					while((tamRead = is.read(buffer))>0){
114 						os.write(buffer, 0, tamRead);
115 	                }
116 					os.flush();
117 					os.close();
118 					is.close();
119 					
120 					Toast.makeText(getApplicationContext(), "Imaxe copiada a " +arquivo_destino.getAbsolutePath(), Toast.LENGTH_LONG).show();
121 				} catch (IOException e) {
122 					// TODO Auto-generated catch block
123 					Toast.makeText(getApplicationContext(), "Houbo un erro na copia da imaxe:"+e.getMessage(), Toast.LENGTH_LONG).show();
124 				}
125 				
126 				
127 			}
128 		});
129 		Button btnCargarImaxe = (Button)findViewById(R.id.UD5_03_btnCargarImaxe);
130 		btnCargarImaxe.setOnClickListener(new OnClickListener() {
131 			
132 			@Override
133 			public void onClick(View arg0) {
134 				// TODO Auto-generated method stub
135 				File ruta = Environment
136 						.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
137 				File arquivo_destino = new File(ruta, "badlogic_copia.jpg");
138 
139 				if (!arquivo_destino.exists()){
140 					Toast.makeText(getApplicationContext(), "O arquivo de imaxe non existe na tarxeta SD. Tes que copialo primeiro!!!", Toast.LENGTH_LONG).show();
141 					return;
142 				}
143 				
144 				ImageView img = (ImageView)findViewById(R.id.UD5_03_imgVwImaxe);
145 				try {
146 					
147 					InputStream is = new FileInputStream(arquivo_destino);
148 					
149 					img.setImageBitmap(BitmapFactory.decodeStream(is));
150 				} catch (FileNotFoundException e) {
151 					// TODO Auto-generated catch block
152 					e.printStackTrace();
153 				}
154 				
155 			}
156 		});
157 		
158 	}
159 	
160 	
161 	@Override
162 	protected void onCreate(Bundle savedInstanceState) {
163 		super.onCreate(savedInstanceState);
164 		setContentView(R.layout.activity_ud5_03__datos_persistentes__arquivos);
165 		
166 		xestionarEventos();
167 	}
168 }
  • Liñas 34-63: Xestionamos o evento de Click sobre o botón Cargar Texto. Pasamos o contido do arquivo /res/raw/pdmavanzadotexto a unha variable de tipo String e pasamos o contido ao EditText.
  • Liñas 72-94: Xestionamos o evento de Click sobre o botón Gardar Texto. Pasamos o contido do EditText a un arquivo na SD, ao cartafol Downloads, de nome "datoscopia.txt".
  • Liñas 105-125: Xestionamos o evento de Click sobre o botón Copiar Imaxe. Pasamos a imaxe de nome pdmavanzado_badlogic.jpg ao cartafol Downloads, có nome "badlogic_copia.jpg".
  • Liñas 135-153: Xestionamos o evento de Click sobre o botón Cargar Imaxe. Cargamos a imaxe do cartafol Downloads, có nome "badlogic_copia.jpg" ao ImageView.






-- Ángel D. Fernández González e Carlos Carrión Álvarez -- (2014).