2010-07-20 2 views
6

Я выпустил приложение на рынке Android, которое с тех пор пришлось снять, потому что примерно половина комментариев были людьми, жалующимися на поврежденные SD-карты. Я несколько раз пересматриваю код и не могу найти ничего, что могло бы повредить SD-карту. Все, что происходит с внешним хранилищем, - это сохранение потоков в виде изображений, которые затем считываются в ImageView.Мое приложение для Android повреждает SD-карты

Это то, что вызывается в корневой активности для создания папок. Пути каталогов хранятся в общедоступных статических переменных.

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

Ниже приведен код для загрузки изображений и их копирования (когда они перемещаются в сохраненную директорию).

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

И это фоновый поток для сети I/O

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

Где перезарядка является работоспособным, чтобы обновить адаптер, уменьшенных изображений представляет собой массив строк ссылки и имя изображения является уникальным 10- 11-значный номер с расширением изображения (.jpeg, .png, .gif специально).

И это аналогичный код работает на фоне асинтетики.

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

Единственный случай, я мог бы найти поврежденных SD карт http://code.google.com/p/android/issues/detail?id=2500

Приложение построено на Android 1.6 и ошибка не recreatable через эмулятор или личного тестирования на 2.1update1 с HTC Desire.

EDIT: Я рассмотрел некоторые другие вопросы и могли ли возникать проблемы из-за того, что я не смывал буферные выходные потоки? Это большая проблема?

+0

Что вы имеете в виде поврежденной SDcards? Что именно происходит? – Macarse

+0

Когда пользователь получает сообщение «Коррумпированная SD-карта», когда он монтируется, и он должен отформатировать его, чтобы заставить его работать снова. – daniel

+0

ohhh! Опасный код: | хе-хе! – Jorgesys

ответ

2

Я вижу две вещи, которые могут быть связаны:

  • Вы должны вызвать .close() на потоки внутри finally{ } блока, так что они закрыты в случае ошибки или принудительное закрытие во время записи.
  • Ловля OutOfMemoryError обычно не очень хорошая идея. У VM закончилась память, и многие вещи будут в плохом непредсказуемом состоянии, лучше прервать в этих случаях.

Моя ставка на finally блоке, возможно, связанных с OutOfMemoryError происходит и не прерывающимся приложение из-за catch, в результате чего некоторые ошибки дальше вниз.

+0

Спасибо. Я буду реализовывать изменения и повторно загружать их на более мелкий рынок и видеть, возникают ли те же проблемы. – daniel

0

Я предполагаю, что вы говорите о повреждении файловой системы, а не об аппаратном повреждении SD-карты. Приложения для Android работают в песочнице, поэтому я думаю, что было бы (практически) невозможно, чтобы приложение повредило файловую систему.

Возможно, это специфическая ошибка для Android, которая запускается чем-то, что делает ваш код.

Я должен согласиться с тем, что копирование байтов - это плохая практика. Вы должны попробовать скопировать блоки размером 512 или 1024 байта.

Также я хотел бы использовать внутреннюю память для TMP файлов: Android Storage Options

Смежные вопросы