Android-використовує файлову систему, яка схожа на дискові файлові систем інших платформ. Даний урок описує, як працювати з файловою системою Android для читання і запису файлів з API File.
Об'єкт File підходить для читання або запису великих обсягів даних від початку до кінця, не пропускаючи нічого навколо. Наприклад, це добре для графічних файлів або для обміну в мережі.
Даний урок показує, як виконувати основні завдання, пов'язані з файлами, у вашому додатку. Урок припускає, що ви знайомі з основами файлової системи Linux і з API стандартного введення/виведення файлів в java.io.
Вибір внутрішнього або зовнішнього сховища
Всі пристрої Android мають дві області зберігання файлів: "internal" і "external" сховище. Ці імена приходять від більш ранніх днів існування Android, коли більшість пристроїв пропонували вбудовану незалежну пам'ять (внутрішню пам'ять), плюс знімний носій, такий як мікро SD-карта (зовнішнє сховище). Деякі пристрої ділять постійні місця зберігання на "internal" і "external" розділи, так що навіть без знімного носія даних завжди є два обсяги сховищ і поводження API є такий же, незалежно є зовнішній накопичувач знімним чи ні. Нижче перераховані узагальнюючі факти про кожний простір збереження.
Внутрішня пам'ять:
• Завжди доступна.
• Файли, збережені в ній, є доступними лише, коли ваш додаток за замовчуванням.
• Коли користувач видаляє ваш додаток, то система видаляє всі файли вашого додатку з внутрішньої пам'яті.
Внутрішня пам'ять краще, якщо хочете бути впевнені, що ні користувач, ні інші додатки не можуть отримати доступ до файлів.
Зовнішні сховища:
• Не завжди доступні, тому що користувач може встановити зовнішній накопичувач USB як сховище, але в деяких випадках видалити його з пристрою.
• Це читання всім світом, тому файли, збережені тут, можна прочитати поза вашим контролем.
• Коли користувач видаляє ваш додаток, система видаляє файли вашого додатку звідси тільки тоді, коли ви зберігаєте їх у каталозі з getExternalFilesDir().
Зовнішні накопичувачі є найкращим місцем для файлів, які не вимагають обмеження доступу і для файлів, якими хочете поділитися з іншими додатками або дозволити користувачеві доступ з комп'ютера.
Порада: Незважаючи на те, додатки встановлюються на внутрішній пам'яті за замовчуванням, ви можете вказати атрибут android:installLocation у вашому маніфесті, так що ваш додаток може бути встановлений на зовнішньому сховищі. Користувачі цінують цю опцію, якщо розмір APK дуже великий, і вони мають простір на зовнішньому сховищі, який більший, ніж внутрішня пам'ять. Для отримання додаткової інформації див. App Install Location.
Отримання дозволів для зовнішнього сховища
Для запису на зовнішньому сховищі, необхідно просити дозвіл WRITE_EXTERNAL_STORAGE у файлі маніфесту:
<manifest ...>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
...
</manifest>
Увага: В даний час, усі додатки мають можливість читати із зовнішнього сховища без спеціального дозволу. Тим не менш, ця ситуація зміниться в майбутніх версіях. Якщо ваш додаток потребує прочитати із зовнішнього сховища (але не писати в нього), то вам потрібно буде оголосити дозвіл READ_EXTERNAL_STORAGE. Щоб переконатися, що ваш додаток продовжує працювати, як і очікувалося, ви повинні оголосити цей дозвіл зараз, перш ніж зміни вступлять в силу.
<manifest ...>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
...
</manifest>
Однак, якщо ваш додаток використовує дозвіл WRITE_EXTERNAL_STORAGE, то неявно маєте також дозвіл на читання із зовнішнього сховища.
Вам не потрібно ніяких дозволів, щоб зберегти файли на внутрішній пам'яті. Ваш додаток завжди має дозвіл на читання і запис файлів в каталозі внутрішнього зберігання.
Збереження файлу у внутрішній пам'яті
При збереженні файлу у внутрішньої пам'яті можете отримати відповідний каталог як File, викликавши його одним з двох методів:
getFilesDir()
Повертає File, який представляє внутрішній каталог для вашого додатку.
getCacheDir()
Повертає File, який представляє внутрішній каталог для тимчасових файлів кеша вашого додатку. Переконайтеся, що видалили всі файли, як тільки вони стали більше не потрібними і задали розумне значення розміру для обсягу пам'яті, яку використовуєте в будь-який момент часу, наприклад, 1 Мб. Якщо в системі починає закінчуватися пам’ять в сховищі, то вона може видалити файли кешу без попередження.
Щоб створити новий файл в одному з цих каталогів, можете використовувати конструктор File(), передаючи файл, наданий одним із зазначених вище способів, який вказує ваш внутрішній каталог зберігання. Наприклад:
File file = new File(context.getFilesDir(), filename);
Крім того, можете викликати openFileOutput(), щоб отримати FileOutputStream, який записує до файлу у вашому внутрішньому каталозі. Приклад, як записати текст у файл:
String filename = "myfile";
String string = "Hello world!";
FileOutputStream outputStream;
try {
outputStream = openFileOutput(filename, Context.MODE_PRIVATE);
outputStream.write(string.getBytes());
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
Або, якщо вам потрібно кешувати деякі файли, то повинні замість цього використовувати createTempFile(). Наприклад, наступний метод витягує ім'я файлу з URL і створює файл з таким ім'ям у внутрішньому каталозі кеша вашого додатка:
public File getTempFile(Context context, String url) {
File file;
try {
String fileName = Uri.parse(url).getLastPathSegment();
file = File.createTempFile(fileName, null, context.getCacheDir());
catch (IOException e) {
// Помилка при створенні файлу
}
return file;
}
Примітка: Внутрішній каталог зберігання вашого додатку вказаний в імені пакета вашого додатку в спеціальному місці файлової системи Android. Технічно, інший додаток може читати ваші внутрішні файли, якщо ви встановите режим файлу, щоб він був читабельним. Тим не менше, інші додатки також повинні знати ім'я пакету вашого додатку та імена файлів. Інші додатки не можуть переглядати ваші внутрішні каталоги і не мають доступ для читання або запису, якщо ви явно не встановили дозвіл для файлів на читання або запис. Тому, поки використовуєте MODE_PRIVATE для своїх файлів на внутрішній пам'яті, вони ніколи не доступні для інших додатків.
Збереження файлу на зовнішньому сховищі
Тому, що зовнішнє сховище може бути недоступне, наприклад, коли користувач встановив сховище на ПК або видалив карту пам'яті SD, що забезпечувала зовнішнє сховище, то завжди повинні переконатися, що пам'ять доступна, перш ніж звертатися до неї. Ви можете запитати стан зовнішнього сховища, викликавши getExternalStorageState(). Якщо повернутий стан MEDIA_MOUNTED, то можете читати і записувати файли. Наприклад, наступні методи корисні для визначення доступності сховища:
/* Перевірка, чи доступне зовнішнє сховище для читання та запису */
public boolean isExternalStorageWritable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
return true;
}
return false;
}
/* Перевірка, чи доступне зовнішнє сховище принаймі для читання */
public boolean isExternalStorageReadable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state) ||
Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
return true;
}
return false;
}
Хоча зовнішнє сховище може змінюватися користувачем та іншими додатками, є дві категорії файлів, які ви могли б на ньому зберігати:
Публічні файли
Файли, які повинні бути вільно доступні для інших додатків і користувачів. Коли користувач видаляє ваш додаток, ці файли повинні залишатися доступними для користувача.
Наприклад, фотографії, виконані за допомогою додатку, або інші завантажені файли.
Приватні файли
Файли, які по праву належать до вашого додатку і повинні бути видалені, коли користувач видаляє ваш додаток. Хоча ці файли є технічно доступними користувачеві і іншим додаткам, тому що вони знаходяться на зовнішньому сховищі, але це файли, які реально не мають цінності для користувача поза вашим додатком. Коли користувач видаляє ваш додаток, система видаляє всі файли в зовнішньому приватному каталозі вашого додатку. Наприклад, додаткові ресурси, завантажені вашим додатком, або тимчасові мультимедійні файли.
Якщо хочете зберігати публічні файли на зовнішньому сховищі, то використовуйте метод getExternalStoragePublicDirectory(), щоб отримати File, який представляє відповідний каталог на зовнішньому сховищі. Метод приймає аргумент, що вказує тип файлу, який хочете зберегти, тому вони можуть бути логічно організовані з іншими такими публічними файлами, як DIRECTORY_MUSIC або DIRECTORY_PICTURES. Наприклад:
public File getAlbumStorageDir(String albumName) {
// Отримати каталог для публічного каталогу фотографій користувача.
File file = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES), albumName);
if (!file.mkdirs()) {
Log.e(LOG_TAG, "Directory not created");
}
return file;
}
Якщо хочете зберегти файли, які є приватними для вашого додатку, то можете створити відповідний каталог, викликавши getExternalFilesDir() і передавши йому ім'я, яке вказує бажаний тип каталогу. Кожен каталог, що створюється таким чином, додається в батьківський каталог, який інкапсулює файли вашого додатку для зовнішнього сховища, які система видаляє, коли користувач видаляє ваш додаток.
Приклад методу, який можете використати, щоб створити каталог для окремого фотоальбому:
public File getAlbumStorageDir(Context context, String albumName) {
// Отримати каталог для приватного каталогу зображень додатку.
File file = new File(context.getExternalFilesDir(
Environment.DIRECTORY_PICTURES), albumName);
if (!file.mkdirs()) {
Log.e(LOG_TAG, "Directory not created");
}
return file;
}
Якщо жодне із імен зумовлених суб-каталогів не задовольняють ваші файли, то можете замість цього викликати getExternalFilesDir() і передати null. Це поверне кореневий каталог для приватного каталогу вашого додатку на зовнішньому сховищі.
Пам'ятайте, що getExternalFilesDir() створює каталог всередині каталогу, який видаляється, коли користувач видаляє ваш додаток. Якщо файли, які ви зберігаєте, повинні залишатися доступними після того, як користувач видаляє ваш додаток, наприклад, тоді, коли додаток - камера і користувач захоче зберегти фотографії, то ви повинні використовувати getExternalStoragePublicDirectory().
Незалежно від того, використовуєте ви getExternalStoragePublicDirectory() для файлів, які є спільними, чи getExternalFilesDir() для файлів, які є приватними для вашого додатку, важливо, що використовуєте імена каталогів, надані такими константами API, як DIRECTORY_PICTURES. Ці імена каталогів дають впевненість, що файли розглядаються в системі належним чином. Наприклад, файли, збережені в DIRECTORY_RINGTONES класифікуються сканером мультимедійних засобів системи, як рінгтони замість музики.
Запит вільного простору
Якщо знаєте заздалегідь, скільки даних зберігаєте, то можете дізнатися, чи достатньо вільного місця, не отримуючи IOException, викликаючи getFreeSpace() або getTotalSpace(). Ці методи надають поточний доступний простір і спільний простір на сховищі, відповідно. Дана інформація також корисна, щоб уникнути заповнення обсягу сховища вище певного порогу.
Тим не менш, система не гарантує, що ви можете записати стільки байтів, скільки позначено getFreeSpace(). Якщо повертається значення на декількох МБ більше, ніж розмір даних, які хочете зберегти, або якщо файлова система заповнена менше, ніж на 90%, то, напевно, збереження можна продовжити. В іншому випадку, ймовірно, ви не повинні записувати в сховище.
Примітка: Ви не зобов'язані перевіряти обсяг вільного простору, перш ніж зберігати файл. Ви можете спробувати записати файл відразу та зловити IOException, якщо обсяг недостатній. Ви, можливо, повинні зробити це, якщо не знаєте точно, скільки місця потрібно. Наприклад, якщо змінили кодування файлу, перш ніж зберігати його, конвертувавши зображення з формату PNG в JPEG, то не знаєте розмір файлу заздалегідь.
Видалення файлу
Ви завжди повинні видаляти файли, які більше не потрібні. Найпростіший спосіб видалити файл, це відкрити посилання на файл і викликати delete() для нього:
myFile.delete();
Якщо файл зберігається на внутрішній пам'яті, ви також можете попросити Context, щоб знайти і видалити файл за допомогою виклику deleteFile():
myContext.deleteFile(fileName);
Примітка: Коли користувач видаляє ваш додаток, то Android система видаляє наступне:
• Всі файли, збережені у внутрішній пам'яті
• Всі файли, збережені на зовнішньому сховищі з використанням getExternalFilesDir().
Тим не менш, ви повинні вручну видаляти всі кешовані файли, створені за допомогою getCacheDir() на регулярній основі, а також регулярно видаляти інші файли, які більше не потрібні.
(Джерело: developer.android.com)