Hilo General - Dudas de Java

Lecherito

#237 Sinceramente, con tan solo mirarte el GroupLayout ya lo tienes prácticamente todo hecho. Se alinean solos y si les pones autoresize (vertical u horizonal) lo tienes easy and fast.

Nihon

Hola gente, estoy teniendo un problema un tanto serio y tocapelotas. Estoy desarrollando una aplicación para android a través de java y por eso lo voy a poner aquí a ver si alguien me puede ayudar.

Una de las cosas que hace mi aplicación es hacer fotos y guardarlas en la SD. Para que esas fotos no ocupen mucho espacio lo que hago es comprimir las fotos una vez que las he tomado de esta manera:

try
{
  ////Aquí parece que hay algún problema que hace petar la aplicación
		FileOutputStream out = new FileOutputStream(file.getAbsolutePath());
		Bitmap bmp = Bitmap.createScaledBitmap(mybit, (int)(mybit.getWidth() / 2),(int)(mybit.getHeight() / 2), false);
		bmp.compress(Bitmap.CompressFormat.JPEG, 60, out);
		out.flush(); //Esto lo acabo de poner al verlo en algunos ejemplos, pero como me sigue sin fallar no se si es algo que ayude.
		out.close();
 ////
				 	
	Camara();
}

El caso es que a mí me falló un par de veces hace tiempo pero no me ha vuelto a pasar. El problema ha venido ahora que la empresa ha repartido la aplicación a los trabajadores y hay algunos a los que sí les falla. (la aplicación cierra al fallar y se pierden ciertos datos). El caso es que habiendo móviles con mi aplicación repartidos por media España hace que no pueda ver cada caso en particular y tenga que volver a preguntarme si mi código está bien o donde falla.

Agradezco cualquier tipo de ayuda ya que si a mí no me falla no puedo saber que tengo un problema ni donde.

1 respuesta
JuAn4k4

#242
Editado: Estoy casi seguro que es por no usar buffer.
http://developer.android.com/reference/java/io/FileOutputStream.html

Yo creo que sera por memoria, por no usar buffer en el Outputstream.
Prueba a usar buffer (igual la imagen es muy grande o el movil una patata y peta por memoria)

File file = ...
   OutputStream out = null;
   try {
     out = new BufferedOutputStream(new FileOutputStream(file));
     ...
    finally {
     if (out != null) {
       out.close();
     }
   }

Cierra al final siempre, en un finally (y casi en otro try/cach) parecido al ejemplo, que los streams suelen pillar memoria (desconozco la impl de android) pero por su ejemplo yo diria que también.

Por cierto, bmp.compress te devuelve true/false si ha ido bien o mal la cosa.

1 1 respuesta
Nihon

#243 Gracias, he cambiado mi código y he añadido un buffer y cambiado File por OutputStream además de cambiar algunas cosas. Como dices, el uso del buffer es probablemente la causa del fallo. A mí me sigue funcionando todo bien, así que no puedo comprobar realmente si lo que he hecho es la solución pero por lo menos me has ayudado a ver el posible error.

Edit: Acabo de probar y me ha fallado, aunque no me ha salido que tipo de error, al tomar la 4ª foto ha petado y se ha cerrado . Además no ha hecho la compresión y ha guardado la imagen original... estoy un poco perdido.

1 respuesta
bLero

#244 ¿Te falla unas veces y otras no? ¿Puedes pegar la traza del error?

Fíjate en esto:

public BufferedOutputStream (OutputStream out)


Constructs a new BufferedOutputStream, providing out with a buffer of 8192 bytes.

public BufferedOutputStream (OutputStream out, int size)


Constructs a new BufferedOutputStream, providing out with size bytes of buffer.

Deberías utilizar el segundo constructor con un tamaño de buffer suficiente.

1 respuesta
Nihon

#245 El problema es que a mí (con 2 móviles distintos) no me falla casi nunca (hoy después de un par de meses y tras hacer 4 fotos una de tantas veces que he probado) pero hay un par de empleados en Madrid a los que sí les falla. Además no puedo hacer la traza ni ponerlos en modo debug porque no tengo los móviles.

Voy a probar el segundo constructor con un tamaño mayor a ver que tal. Aunque también voy a probar a ver si es que intento hacer 2 cosas a la vez (guardar y comprimir) y peta o bien intentar con BitmapFactory que también lo usan en algunos ejemplos.

1 respuesta
JuAn4k4

#246 Que hace "Camara();" ?? Acabo de verlo.

¿ Cierras los streams bien ?

Si es a la 4ª, es que tiene que ser por memoria imagino, tu dale varias veces. 8K de buffer, no estanto para un movil..

Tendrás que mirar qué version android tienen los moviles que fallan, que modelo son y cuanta memoria tienen.

Pon tu codigo final aqui a ver.

Puedes poner un Toast o mensaje con el error o algo ? Para ver la excepción, ayudaria mucho.

1 respuesta
Nihon

#247 Camara() lo único que hace es volver a tomar una foto. Todo el proceso de compresión lo hace al guardar la imagen sin salir de la cámara. Si se ha pulsado guardar, guarda la imágen y sigue con la cámara abierta para tomar otra foto.

Pero lo he puesto en una función aparte y ahora siempre peta a la segunda por lo que ya queda claro que es algo de memoria. Como estoy apunto de salir del curro voy a poner el código tal como me ha quedado después de un millón de cambios y cuando pueda más tarde intento sacar algún error.

La aplicación está desarrollada para Android 2.3.6 y los móviles que hemos comprado son Samsung Galaxy Ace2 aunque no debería ser problema si mañana cualquier otro empleado se la instala en su móvil.

Deciros otra vez que muchas gracias ya que esto me ha pillado de improviso y es algo que necesitan en el trabajo, no es que sea una simple práctica.

	 public void Comprimir(String ruta) //simplemente la ruta de la imagen.
	 {
	 	 try
	 	 { 
			OutputStream os = null;
			Bitmap mybit;
			mybit = BitmapFactory.decodeFile(ruta);
			os = new BufferedOutputStream(new FileOutputStream(ruta));
		 	
		Bitmap bmp = Bitmap.createScaledBitmap(mybit, (int)(mybit.getWidth() / 2),(int)(mybit.getHeight() / 2), false);
	 	bmp.compress(Bitmap.CompressFormat.JPEG, 60, os);
	 	os.flush();
	 	if(os!=null)
	 		os.close();
	 	
 	 }
 	 catch(IOException e)
 	 {
 		 Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
 	 }
 }
1 respuesta
JuAn4k4

#248 prueba con esto

public void Comprimir(String ruta) //simplemente la ruta de la imagen.
{
  OutputStream os = null;
   try
   { 
      Bitmap mybit;
      mybit = BitmapFactory.decodeFile(ruta);
      if (mybit != null) {
        os = new BufferedOutputStream(new FileOutputStream(ruta));              
Bitmap bmp = Bitmap.createScaledBitmap(mybit, (int)(mybit.getWidth() / 2),(int)(mybit.getHeight() / 2), false); if (bmp.compress(Bitmap.CompressFormat.JPEG, 60, os)) { Toast.makeText(this, "Photo has been saved", Toast.LENGTH_LONG).show(); } else { Toast.makeText(this, "Can not save photo", Toast.LENGTH_LONG).show(); } } else { Toast.makeText(this, "Photo does not exist on rute: " + ruta, Toast.LENGTH_LONG).show(); } } catch(IOException e) { Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show(); } finally { if(os!=null) { try { os.close(); } catch(IOException e2) { Toast.makeText(this, "Error closing os: " + e2.getMessage(), Toast.LENGTH_LONG).show(); } } } }
1 respuesta
Lecherito

Tengo un Runnable que lo ejecuto con un Thread(Runnable). Ese runnable llama a un método ajeno a mi. Como podría parar el thread si no puedo hacer un thread.interrupt() (y en el método comprobar que se ha interrumpido o no).

Resumen: Quiero interrumpir/parar un thread(runnable) que sólo tiene un método y solo se ejecuta una vez (no tengo acceso al código del método).

PD: Con threadname.stop(); me funciona pero está deprecated y me lanza una excepción que no me gusta nada.

PD2: Si sabeis las consecuencias de hacer el threadname.stop() agradecería que las pusiérais.

1 respuesta
bLero

#250 usa una variable a modo de semáforo.

y mírate esto:

http://docs.oracle.com/javase/1.5.0/docs/guide/misc/threadPrimitiveDeprecation.html

1 respuesta
Lecherito

#251 Pero como te digo, no puedo usar la variable semáforo ya que no tengo acceso al código que se llama dentro del runnable por lo tanto no puedo usar esa variable semáforo. Además de que el código se ejecuta solamente una vez, aunque sea un código largo.

1 respuesta
bLero

#252 si no quieres terminar el hilo, puedes hacerlo con wait() para pararlo y notify() para resumirlo.

Aunque lo mejor es lo que te digo, una variable volatile que te sirva de flag, si no tienes acceso al código pues lo creas. Un método que modifique el flag y listo.

Te dejo un pequeño ejemplo:

public class MyThread implements Runnable {

private volatile boolean running = true;

public void terminate() {
    running = false;
}

@Override
public void run() {
    while (running) {
        try {
           // some stuff
        } catch (InterruptedException e) {
            running = false;
        }
    }

}
}
public class MyClass {

private Thread thread = null;
private MyThread runnable = null;

public void runThread {
    runnable = new MyThread();
    thread = new Thread(runnable);
    thread.start();
}

public void stopThread() {
    if (thread != null) {
        runnable.terminate();
        thread.join();
    }
}
}

Esto último nose si te servirá pues dices que lo vas a ejecutar una sola vez, así que me imagino que no tendrá un bucle largo para pararlo, en ese caso creo que lo mejor será wait() y notify() o Thread.interrupt() si te lo quieres cargar.

2 respuestas
Nihon

#249 Acabo de probar pero me sigue fallando aunque descartando partes de código he llegado a encontrar la línea donde falla:

mb = BitmapFactory.decodeFile(ruta);

Y es muy extraño que sea aquí ya que es la manera que tengo de decirle al programa que abra la ruta que le paso como una imagen. En fin, nonsense.

2 respuestas
JuAn4k4

#254
Prueba a usar decodeFile con las opciones que lleva:

http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html

Aquí hay uno que puso un trozo de código que dice que dice que "nunca falla":

http://stackoverflow.com/questions/15247565/out-of-memory-error-bitmapfactory-decodestream

Edit: He encontrado algo interesante

http://developer.android.com/training/displaying-bitmaps/index.html

Bitmaps take up a lot of memory, especially for rich images like photographs. For example, the camera on the Galaxy Nexus takes photos up to 2592x1936 pixels (5 megapixels). If the bitmap configuration used is ARGB_8888 (the default from the Android 2.3 onward) then loading this image into memory takes about 19MB of memory (259219364 bytes), immediately exhausting the per-app limit on some devices.

Solucion:

http://developer.android.com/training/displaying-bitmaps/load-bitmap.html

Utiliza en las opciones "inJustDecodeBounds" para el primer bitmap, no te ocupa memoria, y te devuelve el alto y ancho (que es lo que necesitas para despues escalar)

Y esta respuesta es la que mejor me ha parecido
http://stackoverflow.com/questions/3331527/android-resize-a-large-bitmap-file-to-scaled-output-file/8497703#8497703

Nunca he programado android , asi que igual lo sabes mejor que yo y a partir de la documentación te sacas algo propio.

1
Tig

#253 ojo con los hilos en Android, si los declaráis como clases internas no estáticas podéis leakear el contexto. En tu caso no porque declaras la clase en un fichero aparte, pero lo pongo porque es un recurso interesante ^^

http://www.androiddesignpatterns.com/2013/04/activitys-threads-memory-leaks.html

#254 si me acuerdo el lunes copio y pego código que tengo en el curro para hacer lo que creo que quieres.

1 1 respuesta
Lecherito

#253 Lo que quiero es ejecutar código ajeno en mi programa, vamos, yo compilo y ejecuto el código del método main(), ahí ya puede haber lo que sea dentro, yo no lo sabré y quiero saber si hay alguna forma de pararlo.

No lo había pensado como lo has puesto tú con el try catch y la InterruptedException, probaré.

Nihon

#256 Gracias, muy amable. Suelo usar otro hilo a la hora de conectarme al servidor para subir y bajar archivos pero no pensaba que me daría problemas la compresión de fotos, más que nada porque a mí no me ha dado ningún problema haciendo pruebas y también parece ser que no les pasa a todos. Por eso este tema me ha pillado en pelotas como aquel que dice.

1 respuesta
Tig

#258 ¿Qué usas para conectarte con el servidor? ¿Service? ¿Loaders? ¿Thread?

1 1 respuesta
Nihon

#259 Me conecto por FTP haciendo la conexión desde otro hilo con AsyncTask. Me conecto, subo/bajo, cierro la conexión y vuelvo al hilo principal. El tema de comprimir las imágenes es que es mucho mejor envíar imágenes de 300kb por FTP y conexión de datos antes que envíar fotos de 2MB, por eso las comprimo. Pero todo eso se hace en pantallas separadas.

1 respuesta
elkaoD

Perdonad que os interrumpa en mitad de la resolución de la duda.

Este hilo se ha convertido en algo monstruoso y ya no hay quien encuentre información aquí. La mayoría de dudas son avanzadas y seguro que otros usuarios (y las búsquedas desde Google) agradecerán que esté organizado en hilos con su #1 exponiendo la duda y su solución en las respuestas (en lugar de tener que bucear entre páginas y páginas de información).

#260 no sé si al final tenías resuelta la duda o no, ¡pero se merece su propio hilo! Si sigues necesitando un cable te animo a que abras tu propio hilo donde tengamos toda la ayuda que has recibido más organizada.

Puedes simplemente abrir un hilo y enlazar a http://www.mediavida.com/foro/dev/hilo-general-dudas-de-java-435123/9#242 para que se lean las respuestas hasta ahora, y que el resto de respuestas ya vayan en su sitio.

Siento que te haya tocado ser el "conejillo de indias" pero en algún sitio tenía que cortar :)

Dicho esto, cierro for great justice.

Tema cerrado