page.title=Сохранение файлов page.tags=хранение данных helpoutsWidget=true trainingnavtop=true @jd:body
Операционная система Android использует файловую систему, похожую на дисковые файловые системы других платформ. В этом уроке рассказывается, как работать с файловой системой Android для чтения и записи файлов в {@link java.io.File} API-интерфейсах.
Объект {@link java.io.File} подходит для чтения или записи больших объемов данных в порядке от начала к концу без пропусков. Например, этот способ оптимален для изображений или любых других файлов, передаваемых по сети.
В этом уроке вы узнаете, как выполнять в приложении базовые задачи, связанные с файлами. Для прохождения урока вы должны быть знакомы с основами файловой системы Linux и стандартными файловыми API-интерфейсами ввода/вывода в {@link java.io}.
Все устройства Android имеют две области хранения файлов: внутренняя память и внешние хранилища. Эти области появились в первые годы существования Android, когда на большинстве устройств имелись встроенная память (внутреннее хранилище) и карты памяти (например micro SD, внешнее хранилище). Некоторые устройства делят встроенную память на внутренний и внешний разделы, так что даже без съемных носителей в системе две области хранения файлов, и API-интерфейс работает одинаково вне зависимости от типа внешнего хранилища. Ниже подробно описаны обе области хранения файлов.
Внутренняя память
Внутренняя память лучше всего подходит для ситуаций, когда вы хотите быть уверены, что ни пользователь, ни другие приложения не смогут получить доступ к вашим файлам.
Внешнее хранилище
Внешнее хранилище лучше всего подходит для файлов без ограничений доступа и для файлов, которые вы хотите сделать доступными другим приложениям или пользователю через компьютер.
Совет. Хотя по умолчанию приложения устанавливаются во внутреннюю память, вы можете указать в манифесте атрибут {@code android:installLocation}, чтобы приложение можно было установить во внешнее хранилище. Этот вариант будет полезен пользователям при большом размере файла APK, либо если у них есть доступ к внешнему хранилищу, объем которого превышает внутреннюю память. Дополнительную информацию см. в разделе Место установки приложения.
Для записи во внешнее хранилище следует указать запрос разрешения {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} в файле манифеста:
<manifest ...> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> ... </manifest>
Внимание! В настоящее время все приложения могут считывать данные из внешних хранилищ без специального разрешения. Однако в новой версии эта ситуация будет изменена. Если вашему приложению потребуется считать данные из внешнего хранилища (но не записать туда данные), вам необходимо будет декларировать разрешение {@link android.Manifest.permission#READ_EXTERNAL_STORAGE}. Чтобы обеспечить дальнейшую работу вашего приложения ожидаемым образом, вы должны сразу декларировать это разрешение до того, как изменения вступят в силу.
<manifest ...> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> ... </manifest>
Однако если ваше приложение использует разрешение {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE}, оно косвенно получает разрешение на чтение данных из внешнего хранилища.
Для сохранения файлов во внутреннем хранилище не требуется никаких разрешений. У вашего приложения всегда будет разрешение на чтение и запись файлов в его каталог внутренней памяти.
При сохранении файла во внутреннюю память вы можете получить соответствующую директорию в виде {@link java.io.File}, вызвав один из двух методов:
Для создания файла в одной из этих директорий можно использовать конструктор {@link java.io.File#File(File,String) File()}, который передает элемент {@link java.io.File}, предоставляемый одним из вышеприведенных методов, с помощью которого указывается директория во внутренней памяти. Например:
File file = new File(context.getFilesDir(), filename);
Кроме того, можно вызвать метод {@link android.content.Context#openFileOutput openFileOutput()} для получения объекта {@link java.io.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(); }
Если вам потребуется кэшировать какие-то файлы, используйте {@link java.io.File#createTempFile createTempFile()}. Например, следующий метод извлекает имя файла из {@link java.net.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) { // Error while creating file } return file; }
Примечание. Каталог вашего приложения во внутренней памяти указывается с использованием имени пакета приложения в определенном месте файловой системы Android. Технически другое приложение может прочитать ваши файлы во внутренней памяти, если вы установите для файлов режим Readable (доступно для чтения). Однако для этого другому приложению должны быть известны имя пакета вашего приложения и имена файлов. Другие приложения не могут просматривать внутренние каталоги вашего приложения и не имеют разрешений на чтение или запись, если вы специально не установите для своих файлов режим Readable (доступно для чтения) или Writable (доступно для записи). Следовательно, пока вы будете использовать режим {@link android.content.Context#MODE_PRIVATE} для своих файлов во внутренней памяти, они будут недоступны другим приложениям.
Поскольку внешнее хранилище может быть недоступно— например, если пользователь установил хранилище в гнездо на компьютере или извлек из устройства SD-карту, — перед доступом к тому всегда следует проверять его доступность. Состояние внешнего хранилища можно узнать, если вызвать {@link android.os.Environment#getExternalStorageState}. Если возвращаемое состояние равнозначно {@link android.os.Environment#MEDIA_MOUNTED}, вы сможете выполнять с файлами операции чтения и записи. Например, следующие методы будут полезными для определения доступности хранилища данных:
/* Checks if external storage is available for read and write */ public boolean isExternalStorageWritable() { String state = Environment.getExternalStorageState(); if (Environment.MEDIA_MOUNTED.equals(state)) { return true; } return false; } /* Checks if external storage is available to at least read */ public boolean isExternalStorageReadable() { String state = Environment.getExternalStorageState(); if (Environment.MEDIA_MOUNTED.equals(state) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) { return true; } return false; }
Хотя внешнее хранилище может быть изменено пользователем и другими приложениями, существует две категории файлов, которые в нем можно сохранять:
Например, в эту категорию входят снимки, сделанные с помощью вашего приложения, а также другие загружаемые файлы.
Например, к этой категории относятся дополнительные ресурсы, загруженные приложением, и временные мультимедийные файлы.
Если вы хотите сохранить публичные файлы во внешнем хранилище, используйте методы {@link android.os.Environment#getExternalStoragePublicDirectory getExternalStoragePublicDirectory()} для получения {@link java.io.File}, отражающего соответствующий каталог во внешнем хранилище. Этот метод принимает аргумент, указывающий тип сохраняемого файла и позволяющий включить его в логическую структуру с другими публичными файлами, например, {@link android.os.Environment#DIRECTORY_MUSIC} или {@link android.os.Environment#DIRECTORY_PICTURES}. Например:
public File getAlbumStorageDir(String albumName) { // Get the directory for the user's public pictures directory. File file = new File(Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_PICTURES), albumName); if (!file.mkdirs()) { Log.e(LOG_TAG, "Directory not created"); } return file; }
Если вы хотите сохранить личные файлы вашего приложения, вы можете получить соответствующий каталог посредством, вызвав метод {@link android.content.Context#getExternalFilesDir getExternalFilesDir()} и предоставив ему имя с указанием желаемого типа каталога. Каждый каталог, создаваемый таким способом, добавляется в родительский каталог, в котором объединены все файлы вашего приложения во внешнем хранилище. При удалении вашего приложения пользователем система удаляет этот каталог.
Например, следующий метод позволит вам создать каталог для персонального фотоальбома:
public File getAlbumStorageDir(Context context, String albumName) { // Get the directory for the app's private pictures directory. File file = new File(context.getExternalFilesDir( Environment.DIRECTORY_PICTURES), albumName); if (!file.mkdirs()) { Log.e(LOG_TAG, "Directory not created"); } return file; }
Если ни одно из готовых имен подкаталогов не подходит для ваших файлов, вы можете вызвать {@link android.content.Context#getExternalFilesDir getExternalFilesDir()} и передать аргумент {@code null}. При этом будет возвращен корневой каталог закрытого каталога вашего приложения во внешнем хранилище.
Следует помнить, что {@link android.content.Context#getExternalFilesDir getExternalFilesDir()} создает каталог внутри каталога, который удаляется при удалении вашего приложения пользователем. Если сохраняемые вами файлы должны оставаться доступными после удаления вашего приложения пользователем — например, если ваше приложение работает с камерой, а пользователь хочет сохранить снимки, — вам следует использовать {@link android.os.Environment#getExternalStoragePublicDirectory getExternalStoragePublicDirectory()}.
Вне зависимости от того, используете ли вы {@link android.os.Environment#getExternalStoragePublicDirectory getExternalStoragePublicDirectory()} для общих файлов или link android.content.Context#getExternalFilesDir getExternalFilesDir()} для собственных файлов приложения, вы должны использовать имена каталогов, предоставляемые постоянными значениями API-интерфейсов, например, {@link android.os.Environment#DIRECTORY_PICTURES}. Эти имена каталогов обеспечивают правильную обработку файлов системой. Например, сохраненные в {@link android.os.Environment#DIRECTORY_RINGTONES} файлы относятся медиа-сканером системы в категорию рингтонов, а не музыки.
Если вам заранее известен объем сохраняемых данных, вы можете определить наличие достаточного пространства без исключения {@link java.io.IOException}, вызвав метод {@link java.io.File#getFreeSpace} или {@link java.io.File#getTotalSpace}. Эти методы позволяют узнать текущее доступное пространство и общее пространство в хранилище. Эта информация также позволять избежать переполнения объема хранилища сверх определенного уровня.
Однако система не гарантирует возможность записи такого же количества байт, как указано в {@link java.io.File#getFreeSpace}. Если выводимое число на несколько мегабайт превышает размер данных, которые вы хотите сохранить, или если файловая система заполнена менее, чем на 90%, дальше можно действовать спокойно. В противном случае запись в хранилище осуществлять нежелательно.
Примечание. Вам необязательно проверять объем доступного места перед сохранением файла. Вы можете попробовать сразу записать файл, а затем определить событие {@link java.io.IOException}, если оно возникнет. Это может потребоваться, если вы точно не знаете, сколько нужно свободного места. Например, если вы меняете кодировку файла перед сохранением при конвертации изображения PNG в формат JPEG, вы не будете знать размер файла заранее.
Ненужные файлы всегда следует удалять. Наиболее простой способ удалить файл – вызвать в открытом файле ссылку {@link java.io.File#delete} на сам этот файл.
myFile.delete();
Если файл сохранен во внутреннем хранилище, вы также можете запросить {@link android.content.Context}, чтобы найти и удалить файл посредством вызова {@link android.content.Context#deleteFile deleteFile()}:
myContext.deleteFile(fileName);
Примечание. При удалении пользователем вашего приложения система Android удаляет следующие элементы:
Однако вам следует регулярно вручную очищать кэш-память, чтобы удалить файлы, созданные с помощью {@link android.content.Context#getCacheDir()}, а также удалять любые другие ненужные файлы.