2010-07-20 13 views
6

Lancé una aplicación en el mercado de Android que tuve que eliminar porque aproximadamente la mitad de los comentarios eran personas que se quejaban de tarjetas SD dañadas. He revisado el código varias veces y no encuentro nada que pueda dañar una tarjeta SD. Todo lo que sucede con el almacenamiento externo es el almacenamiento de secuencias como imágenes, que luego se leen en ImageView.Mi aplicación para Android daña las tarjetas SD

Esto es lo que se llama en la actividad de raíz para crear las carpetas. Las rutas del directorio se almacenan en variables públicas estáticas.

//Get the SD Card directory 
    String external = Environment.getExternalStorageDirectory().getAbsolutePath() + "/appfolder/"; 

    CACHE_DIRECTORY = external + ".cache/"; 
    SAVED_DIRECTORY = external + "saved/"; 

    File cache = new File(CACHE_DIRECTORY); 
    File saved = new File(SAVED_DIRECTORY); 
    cache.mkdirs(); 
    saved.mkdirs(); 

Y el siguiente es el código para descargar imágenes y copiarlas (para cuando se muevan al directorio guardado).

public static void saveImage(File file, URL url) throws IOException { 
    BufferedInputStream bis = new BufferedInputStream(url.openStream()); 
    BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file)); 
    int bytes; 
    while ((bytes = bis.read()) != -1) { 
     bos.write(bytes); 
    } 
    bos.close(); 
    bis.close(); 
} 

public static void copy(File fileIn, File fileOut) throws IOException { 
    BufferedInputStream bin = new BufferedInputStream(new FileInputStream(fileIn)); 
    BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(fileOut)); 
    int bytes; 
    while ((bytes = bin.read()) != -1) { 
     bout.write(bytes); 
    } 
    bin.close(); 
    bout.close(); 
} 

Y esto es un subproceso de fondo para la red de E/S

public void run() { 
    for (String url : thumbnails) { 
     if (url != null) { 
      String[] urlParts = url.split("/"); 
      String imageName = urlParts[urlParts.length - 1]; 
      File file = new File(Main.CACHE_DIRECTORY + imageName); 
      if (!file.exists() || file.length() == 0) { 
       try { 
        Image.saveImage(file, new URL(url)); 
       } catch (IOException e) {} 
      } 
     actx.runOnUiThread(reload); 
     } 
    } 
} 

Dónde recarga es un ejecutable para actualizar un adaptador, miniaturas es un conjunto de direcciones URL de cuerda y nombre de la imagen es una única 10- Número de 11 dígitos con una extensión de imagen (.jpeg, .png, .gif específicamente).

Y esto es un código similar ejecutado en el fondo de una asynctask.

String imageUrl = (String)params[0]; 
    String[] imageUrlParts = imageUrl.split("/"); 
    String imageName = imageUrlParts[imageUrlParts.length - 1]; 
    URL fullImageUrl; 
    try { 
     fullImageUrl = new URL(imageUrl); 
    } catch (MalformedURLException me) { 
     cancel(true); 
     return null; 
    } 

    File file = new File(Main.CACHE_DIRECTORY + imageName); 
    try { 
     URLConnection ucon = fullImageUrl.openConnection(); 
     int requestedSize = ucon.getContentLength(); 
     long fileSize = file.length(); 
     //Either the file does not exist, or it exists but was cancelled early due to 
     //User or IOException, so it needs to be redownloaded 
     if (!file.exists() || ((file.exists()) && fileSize < (requestedSize * 0.8))) { 
      mLoad.setMax(requestedSize); 
      BufferedInputStream bis = new BufferedInputStream(ucon.getInputStream()); 
      BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(file)); 
      int bytes; 
      int count = 0; 
      while ((bytes = bis.read()) != -1) { 
       bout.write(bytes); 
       count++; 
       //Updates in increments of 2kb 
       if (count % 2048 == 0) { 
        publishProgress(count); 
       } 
      } 
      bis.close(); 
      bout.close(); 
     } 

     if (save) { 
      File saveFile = new File(Main.SAVED_DIRECTORY + imageName); 
      copy(file, saveFile); 
     } 
    } catch (IOException e) { 
     cancel(true); 
     return null; 
    } catch (OutOfMemoryError e) { 
     cancel(true); 
     return null; 
    } 

El único caso que pude encontrar de tarjetas SD dañadas es http://code.google.com/p/android/issues/detail?id=2500

La aplicación se basa en Android 1.6 y el error no está recreables a través de emulador o pruebas de personal en 2.1update1 con HTC Desire.

EDITAR: He analizado algunas otras preguntas y ¿podrían surgir problemas de que no se descarguen los flujos de salida almacenados en el búfer? ¿Eso es un gran problema?

+0

¿Qué quiere decir por SDcards dañados? ¿Qué pasa exactamente? – Macarse

+0

Donde el usuario recibe el mensaje "Tarjeta SD corrupta" cuando se está montando y tienen que formatearlo para que funcione nuevamente. – daniel

+0

ohhh! Código peligroso: | jeje! – Jorgesys

Respuesta

2

veo dos cosas que podrían estar relacionados:

  • Debe llamar .close() en las corrientes dentro de un bloque finally{ } por lo que se cierran en caso de que haya un error o una fuerza cercana al escribir.
  • La captura de OutOfMemoryError no suele ser una buena idea. La máquina virtual se ha quedado sin memoria y muchas cosas estarán en un mal estado impredecible, lo mejor es abortar en esos casos.

Mi apuesta está en el bloque finally, posiblemente relacionado con un OutOfMemoryError sucediendo y no abortar la aplicación debido a la catch, causando algún error más abajo.

+0

Gracias. Implementaré los cambios y los volveré a cargar en un mercado más pequeño para ver si surgen los mismos problemas. – daniel

0

Supongo que está hablando de la corrupción del sistema de archivos y no del daño de hardware de la tarjeta SD. Las aplicaciones de Android se ejecutan en sandbox, por lo que creo que sería (casi) imposible que una aplicación dañe el sistema de archivos.

Probablemente este es un error específico de Android que se desencadena por algo que su código está haciendo.

Tengo que aceptar que copiar bytes es una mala práctica. Debería intentar copiar bloques de 512 o 1024 bytes en su lugar.

También me gustaría utilizar el almacenamiento interno para archivos temporales: Android Storage Options

Cuestiones relacionadas