blob: 2afecea81a8958eaa68353065d6c899c154938e7 [file] [log] [blame]
page.title=Сохранение файлов
page.tags=хранение данных
helpoutsWidget=true
trainingnavtop=true
@jd:body
<div id="tb-wrapper">
<div id="tb">
<h2>Содержание этого урока</h2>
<ol>
<li><a href="#InternalVsExternalStorage">Выбор внутреннего или внешнего хранилища</a></li>
<li><a href="#GetWritePermission">Получение разрешений на доступ к внешнему хранилищу</a></li>
<li><a href="#WriteInternalStorage">Сохранение файла во внутреннем хранилище</a></li>
<li><a href="#WriteExternalStorage">Сохранение файла во внешнем хранилище</a></li>
<li><a href="#GetFreeSpace">Запрос свободного пространства</a></li>
<li><a href="#DeleteFile">Удаление файла</a></li>
</ol>
<h2>См. также:</h2>
<ul>
<li><a href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">Использование внутреннего
хранилища</a></li>
<li><a href="{@docRoot}guide/topics/data/data-storage.html#filesExternal">Использование внешнего
хранилища</a></li>
</ul>
</div>
</div>
<p>Операционная система Android использует файловую систему,
похожую на дисковые файловые системы других платформ. В этом уроке рассказывается,
как работать с файловой системой Android для чтения и записи файлов в {@link java.io.File}
API-интерфейсах.</p>
<p>Объект {@link java.io.File} подходит для чтения или записи больших объемов данных в порядке
от начала к концу без пропусков. Например, этот способ оптимален для изображений или
любых других файлов, передаваемых по сети.</p>
<p>В этом уроке вы узнаете, как выполнять в приложении базовые задачи, связанные с файлами.
Для прохождения урока вы должны быть знакомы с основами файловой системы Linux
и стандартными файловыми API-интерфейсами ввода/вывода в {@link java.io}.</p>
<h2 id="InternalVsExternalStorage">Выбор внутреннего или внешнего хранилища</h2>
<p>Все устройства Android имеют две области хранения файлов: внутренняя память и внешние хранилища. Эти области
появились в первые годы существования Android, когда на большинстве устройств имелись встроенная память
(внутреннее хранилище) и карты памяти (например micro SD, внешнее хранилище).
Некоторые устройства делят встроенную память на внутренний и внешний разделы,
так что даже без съемных носителей в системе две области хранения файлов,
и API-интерфейс работает одинаково вне зависимости от типа внешнего хранилища.
Ниже подробно описаны обе области хранения файлов.</p>
<div class="col-5" style="margin-left:0">
<p><b>Внутренняя память</b></p>
<ul>
<li>Всегда доступна.</li>
<li>Сохраненные здесь файлы по умолчанию доступны только вашему приложению.</li>
<li>При удалении пользователем вашего приложения система Android удаляет из внутренней памяти
все файлы этого приложения.</li>
</ul>
<p>Внутренняя память лучше всего подходит для ситуаций, когда вы хотите быть уверены, что ни пользователь, ни другие приложения не смогут получить
доступ к вашим файлам.</p>
</div>
<div class="col-7" style="margin-right:0">
<p><b>Внешнее хранилище</b></p>
<ul>
<li>Доступно не всегда, потому что пользователь может в любое время подключать и отключать такие
хранилища, например, USB-накопители.</li>
<li>Такие хранилища доступны для чтения везде,
поэтому вы не контролируете чтение сохраненных в них данных.</li>
<li>При удалении пользователем вашего приложения система Android удаляет из внешних хранилищ
файлы этого приложения, только если они сохраняются в директории из {@link android.content.Context#getExternalFilesDir
getExternalFilesDir()}.</li>
</ul>
<p>Внешнее хранилище лучше всего
подходит для файлов без ограничений доступа и для файлов, которые вы хотите сделать
доступными другим приложениям или пользователю через компьютер.</p>
</div>
<p class="note" style="clear:both">
<strong>Совет.</strong> Хотя по умолчанию приложения устанавливаются во внутреннюю память,
вы можете указать в манифесте атрибут <a href="{@docRoot}guide/topics/manifest/manifest-element.html#install">{@code
android:installLocation}</a>, чтобы приложение можно
было установить во внешнее хранилище. Этот вариант будет полезен пользователям при большом размере файла APK, либо если у них есть доступ
к внешнему хранилищу, объем которого превышает внутреннюю память. Дополнительную информацию
см. в разделе <a href="{@docRoot}guide/topics/data/install-location.html">Место установки приложения</a>.</p>
<h2 id="GetWritePermission">Получение разрешений доступа к внешнему хранилищу</h2>
<p>Для записи во внешнее хранилище следует указать запрос
разрешения {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} в <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">файле манифеста</a>:</p>
<pre>
&lt;manifest ...>
&lt;uses-permission android:name=&quot;android.permission.WRITE_EXTERNAL_STORAGE&quot; /&gt;
...
&lt;/manifest>
</pre>
<div class="caution"><p><strong>Внимание!</strong>
В настоящее время все приложения могут считывать данные из внешних
хранилищ без специального разрешения. Однако в новой версии эта ситуация будет изменена. Если вашему приложению потребуется
считать данные из внешнего хранилища (но не записать туда данные), вам необходимо будет декларировать разрешение {@link
android.Manifest.permission#READ_EXTERNAL_STORAGE}. Чтобы обеспечить дальнейшую работу вашего приложения
ожидаемым образом, вы должны сразу декларировать это разрешение до того, как изменения вступят в силу.</p>
<pre>
&lt;manifest ...>
&lt;uses-permission android:name=&quot;android.permission.READ_EXTERNAL_STORAGE&quot; /&gt;
...
&lt;/manifest>
</pre>
<p>Однако если ваше приложение использует разрешение {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE},
оно косвенно получает разрешение на чтение данных из внешнего хранилища.</p>
</div>
<p>Для сохранения файлов во внутреннем хранилище
не требуется никаких разрешений. У вашего приложения всегда будет разрешение на чтение и запись
файлов в его каталог внутренней памяти.</p>
<h2 id="WriteInternalStorage">Сохранение файла во внутренней памяти</h2>
<p>При сохранении файла во внутреннюю память вы можете получить соответствующую директорию в виде
{@link java.io.File}, вызвав один из двух методов:</p>
<dl>
<dt>{@link android.content.Context#getFilesDir}</dt>
<dd>Возвращает {@link java.io.File}, соответствующий внутренней директории приложения.</dd>
<dt>{@link android.content.Context#getCacheDir}</dt>
<dd>Возвращает {@link java.io.File}, соответствующий внутренней директории файлов временной
кэш-памяти приложения. Обязательно удаляйте каждый файл, когда он перестанет
быть нужным, и устанавливайте разумное ограничение размера памяти, используемой в каждый момент времени,
например, 1 МБ. Если системе будет не хватать места в хранилище, она может удалять файлы
из кэш-памяти без уведомления.</dd>
</dl>
<p>Для создания файла в одной из этих директорий можно использовать конструктор {@link
java.io.File#File(File,String) File()}, который передает элемент {@link java.io.File}, предоставляемый одним из вышеприведенных
методов, с помощью которого указывается директория во внутренней памяти. Например:</p>
<pre>
File file = new File(context.getFilesDir(), filename);
</pre>
<p>Кроме того, можно вызвать метод {@link
android.content.Context#openFileOutput openFileOutput()} для получения объекта {@link java.io.FileOutputStream}
, производящего запись в файл во внутренней памяти. Вот пример
записи текста в файл:</p>
<pre>
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();
}
</pre>
<p>Если вам потребуется кэшировать какие-то файлы, используйте {@link
java.io.File#createTempFile createTempFile()}. Например, следующий метод извлекает
имя файла из {@link java.net.URL} и создает файл с этим именем
в каталоге внутренней кэш-памяти вашего приложения:</p>
<pre>
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;
}
</pre>
<p class="note"><strong>Примечание.</strong>
Каталог вашего приложения во внутренней памяти указывается
с использованием имени пакета приложения в определенном месте файловой системы Android.
Технически другое приложение может прочитать ваши файлы во внутренней памяти, если вы установите для файлов
режим Readable (доступно для чтения). Однако для этого другому приложению должны быть известны имя пакета вашего
приложения и имена файлов. Другие приложения не могут просматривать внутренние каталоги вашего приложения и не имеют разрешений на
чтение или запись, если вы специально не установите для своих файлов режим Readable (доступно для чтения) или Writable (доступно для записи). Следовательно, пока
вы будете использовать режим {@link android.content.Context#MODE_PRIVATE} для своих файлов во внутренней памяти,
они будут недоступны другим приложениям.</p>
<h2 id="WriteExternalStorage">Сохранение файла во внешнем хранилище</h2>
<p>Поскольку внешнее хранилище может быть недоступно&mdash; например, если пользователь установил
хранилище в гнездо на компьютере или извлек из устройства SD-карту, &mdash; перед доступом к тому всегда следует
проверять его доступность. Состояние внешнего
хранилища можно узнать, если вызвать {@link android.os.Environment#getExternalStorageState}. Если возвращаемое состояние
равнозначно {@link android.os.Environment#MEDIA_MOUNTED}, вы сможете выполнять с файлами операции чтения и
записи. Например, следующие методы будут полезными для определения доступности
хранилища данных:</p>
<pre>
/* 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;
}
</pre>
<p>Хотя внешнее хранилище может быть изменено пользователем и другими приложениями, существует две
категории файлов, которые в нем можно сохранять:</p>
<dl>
<dt>Общедоступные файлы</dt>
<dd>Файлы, которые
должны быть доступны другим приложениям и пользователю. Когда пользователь удаляет ваше приложение,
эти файлы должны оставаться доступны пользователю.
<p>Например, в эту категорию входят снимки, сделанные с помощью вашего приложения, а также другие загружаемые файлы.</p>
</dd>
<dt>Личные файлы</dt>
<dd>Это файлы, принадлежащие вашему приложению. Они должны удаляться при удалении
вашего приложения пользователем. Хотя технически эти файлы доступны для пользователя и других приложений, поскольку находятся
во внешнем хранилище, они не имеют никакой ценности для пользователей
вне вашего приложения. Когда пользователь удаляет ваше приложение, система удаляет
все файлы из каталога закрытых файлов вашего приложения во внешнем хранилище.
<p>Например, к этой категории относятся дополнительные ресурсы, загруженные приложением, и временные мультимедийные файлы.</p>
</dd>
</dl>
<p>Если вы хотите сохранить публичные файлы во внешнем хранилище, используйте методы
{@link android.os.Environment#getExternalStoragePublicDirectory
getExternalStoragePublicDirectory()} для получения {@link java.io.File}, отражающего
соответствующий каталог во внешнем хранилище. Этот метод принимает аргумент, указывающий
тип сохраняемого файла и позволяющий включить его в логическую структуру
с другими публичными файлами, например, {@link android.os.Environment#DIRECTORY_MUSIC} или {@link
android.os.Environment#DIRECTORY_PICTURES}. Например:</p>
<pre>
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;
}
</pre>
<p>Если вы хотите сохранить личные файлы вашего приложения, вы можете получить
соответствующий каталог посредством, вызвав метод {@link
android.content.Context#getExternalFilesDir getExternalFilesDir()} и предоставив ему имя с указанием
желаемого типа каталога. Каждый каталог, создаваемый таким способом, добавляется в родительский
каталог, в котором объединены все файлы вашего приложения во внешнем хранилище. При удалении вашего приложения
пользователем система удаляет этот каталог.</p>
<p>Например, следующий метод позволит вам создать каталог для персонального фотоальбома:</p>
<pre>
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;
}
</pre>
<p>Если ни одно из готовых имен подкаталогов не подходит для ваших файлов, вы можете вызвать {@link
android.content.Context#getExternalFilesDir getExternalFilesDir()} и передать аргумент {@code null}. При этом
будет возвращен корневой каталог закрытого каталога вашего приложения во внешнем хранилище.</p>
<p>Следует помнить, что {@link android.content.Context#getExternalFilesDir getExternalFilesDir()}
создает каталог внутри каталога, который удаляется при удалении вашего приложения пользователем.
Если сохраняемые вами файлы должны оставаться доступными после удаления вашего
приложения пользователем &mdash; например, если ваше приложение работает с камерой, а пользователь хочет сохранить снимки, &mdash; вам
следует использовать {@link android.os.Environment#getExternalStoragePublicDirectory
getExternalStoragePublicDirectory()}.</p>
<p>Вне зависимости от того, используете ли вы {@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} файлы относятся медиа-сканером системы в категорию рингтонов,
а не музыки.</p>
<h2 id="GetFreeSpace">Запрос доступного пространства</h2>
<p>Если вам заранее известен объем сохраняемых данных, вы можете
определить наличие достаточного пространства без исключения {@link
java.io.IOException}, вызвав метод {@link java.io.File#getFreeSpace} или {@link
java.io.File#getTotalSpace}. Эти методы позволяют узнать текущее доступное пространство и
общее пространство в хранилище. Эта информация также позволять
избежать переполнения объема хранилища сверх определенного уровня.</p>
<p>Однако система не гарантирует возможность записи такого же количества байт, как указано
в {@link java.io.File#getFreeSpace}. Если выводимое число на
несколько мегабайт превышает размер данных, которые вы хотите сохранить, или если файловая система заполнена
менее, чем на 90%, дальше можно действовать спокойно.
В противном случае запись в хранилище осуществлять нежелательно.</p>
<p class="note"><strong>Примечание.</strong> Вам необязательно проверять объем доступного места
перед сохранением файла. Вы можете попробовать сразу записать файл, а затем
определить событие {@link java.io.IOException}, если оно возникнет. Это может потребоваться,
если вы точно не знаете, сколько нужно свободного места. Например, если вы
меняете кодировку файла перед сохранением при конвертации изображения PNG в формат
JPEG, вы не будете знать размер файла заранее.</p>
<h2 id="DeleteFile">Удаление файла</h2>
<p>Ненужные файлы всегда следует удалять. Наиболее простой способ удалить
файл вызвать в открытом файле ссылку {@link java.io.File#delete} на сам этот файл.</p>
<pre>
myFile.delete();
</pre>
<p>Если файл сохранен во внутреннем хранилище, вы также можете запросить {@link android.content.Context}, чтобы найти и
удалить файл посредством вызова {@link android.content.Context#deleteFile deleteFile()}:</p>
<pre>
myContext.deleteFile(fileName);
</pre>
<div class="note">
<p><strong>Примечание.</strong> При удалении пользователем вашего приложения система Android удаляет
следующие элементы:</p>
<ul>
<li>Все файлы, сохраненные во внутреннем хранилище</li>
<li>Все файлы, сохраненные во внешнем хранилище с использованием {@link
android.content.Context#getExternalFilesDir getExternalFilesDir()}.</li>
</ul>
<p>Однако вам следует регулярно вручную очищать кэш-память, чтобы удалить файлы, созданные с помощью
{@link android.content.Context#getCacheDir()}, а также удалять любые
другие ненужные файлы.</p>
</div>