2010-07-20 5 views
6

J'ai publié une application sur le marché Android que j'ai dû retirer depuis, car environ la moitié des commentaires étaient des personnes se plaignant de cartes SD endommagées. Je suis allé sur le code plusieurs fois et ne trouve rien qui pourrait endommager une carte SD. Tout ce qui se passe qui implique un stockage externe est la sauvegarde des flux en tant qu'images, qui sont ensuite lues dans ImageView.Mon application Android endommage les cartes SD

C'est ce que l'on appelle dans l'activité racine pour créer les dossiers. Les chemins de répertoire sont stockés dans des variables statiques publiques.

//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(); 

Et voici le code pour télécharger des images et les copier (lorsqu'elles sont déplacées dans le répertoire sauvegardé).

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(); 
} 

Et c'est un fil d'arrière-plan pour le réseau d'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); 
     } 
    } 
} 

Où reload est un runnable de mettre à jour un adaptateur, les vignettes est un tableau de urls de chaîne et le nom de l'image est un 10 unique, Numéro à 11 chiffres avec une extension d'image (.jpeg, .png, .gif en particulier).

Et c'est un code similaire exécuté en arrière-plan d'une 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; 
    } 

L'instance que je pouvais trouver des cartes SD endommagées est http://code.google.com/p/android/issues/detail?id=2500

L'application est construit sur Android 1.6 et le bug est recréé pas par l'émulateur ou de test personnel sur 2.1update1 avec HTC Desire.

EDIT: J'ai examiné d'autres questions et est-ce que les problèmes pourraient provenir de ce que je ne vidange pas les flux de sortie tamponnés? Est-ce une grosse affaire?

+0

Que voulez-vous dire par SDcards endommagés? Qu'est-ce qui se passe exactement? – Macarse

+0

Lorsque l'utilisateur reçoit le message "Corrupt SD Card" lors de son montage et qu'il doit le formater pour qu'il fonctionne de nouveau. – daniel

+0

ohhh! Code dangereux: | hehe! – Jorgesys

Répondre

2

Je vois deux choses qui pourraient être liées:

  • Vous devez appeler .close() sur les cours d'eau dans un bloc finally{ } de sorte qu'ils sont fermés en cas d'erreur ou la force de fermeture pendant l'écriture.
  • Attraper OutOfMemoryError n'est généralement pas une bonne idée. La VM a manqué de mémoire et beaucoup de choses seront dans un mauvais état imprévisible, préférable d'abandonner dans ces cas.

Mon pari est sur le bloc finally, peut-être lié à un OutOfMemoryError qui se passe et non l'application à cause l'interruption du catch, provoquant une erreur plus bas.

+0

Merci. Je vais mettre en œuvre les changements et le réimplanter sur un marché plus petit et voir si les mêmes problèmes se posent. – daniel

0

Je suppose que vous parlez de la corruption du système de fichiers et non des dommages matériels de la carte SD. Les applications Android s'exécutent dans le bac à sable, donc je pense qu'il serait (presque) impossible pour une application d'endommager le système de fichiers.

Probablement c'est un bug spécifique à l'androïde qui est déclenché par quelque chose que fait votre code.

Je dois admettre que la copie d'octets est une mauvaise pratique. Vous devriez essayer de copier des blocs de 512 ou 1024 octets à la place.

Je voudrais également utiliser le stockage interne pour les fichiers tmp: Android Storage Options

Questions connexes