В этой статье я опишу работу с файлами на Android, попутно вашему вниманию будет представлено несколько полезных методов для работы с потоками. После прочтения этой статьи вы узнаете как:

  • Работать с файлами во внутрней памяти
  • Работать с файлами на SD-карте
  • Превращать InputStream в String
  • Копировать содержимое InputStream в OutputStream

Набор утилитарных методов для работы с потоками

Все мы хотим писать меньше кода. А чтобы писать меньше, нужно повтороно его использовать и речь идет не о копи-пастинге :) Ниже будет представлен листинг класса, которые оболегчит нашу работы:


public class CommonIO {
public final static String LINE_SEPARTOR = System.getProperty("line.separator");
private static final int BUFFER_SIZE = 2048;
private static final int EOF_MARK = -1;
private CommonIO() {
};
public static boolean close(Closeable closeable) {
if (closeable != null) {
try {
closeable.close();
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
}
} else {
return false;
}
}
public static String convertStreamToString(InputStream is) {
try {
return new java.util.Scanner(is).useDelimiter("\A").next();
} catch (java.util.NoSuchElementException e) {
return "";
}
}
public static int writeFromInputToOutput(InputStream source, OutputStream dest) {
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead = EOF_MARK;
int count = 0;
try {
while ((bytesRead = source.read(buffer)) != EOF_MARK) {
dest.write(buffer, 0, bytesRead);
count += bytesRead;
}
} catch (IOException e) {
e.printStackTrace();
}
return count;
}

public static boolean isExternalAvailable() {
return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
}
}

Кратко опишу назначение этих методов:

close(Closeable) Закрывает любой объект, поддерживающий интерфейс Closeable, в том числе: потоки, курсоры, каналы, cокеты и многие другие. Спокойно примет null в качестве параметра.
convertStreamToString(InputStream) Выполнит преобразование InputStream к строке. Является одной из реализаций трюков со Scanner. Использует дефолтную кодировку системы (понимаете к чему я клоню?).
writeFromInputToOutput(source, dest) Инкапсулирует рутинную работы по копированию данных из InputStream в OutputStream.
isExternalAvailable() Проверяет доступность внешней памяти — SD-карты.

Далее по тексу вы увидите, сколько строк кода нам это сэкономит и от скольких потенциальных ошибок убережет.

Работа с файлами во внутренней памяти

Начнем с вопроса: где будет лежать записанный во внутреннюю память файл?

Ответ: локация файла опрделяется приложением его создавшим, и описывается так:

/data/data/<packagename>/<filename>

К сожалению (или к счастью?), выдирать файлы из памяти девайса стандартными методами нельзя, но можно с эмулятора, приведу иллюстрацию к вышеописанной схеме расположения файлов.

 Распложение файлов во внутренней памяти

Иерархия распложения файлов во внутрнней памяти. Пакет приложения ‘by.idev.tools’, имя файла ‘menudomenu’. Изображение получено при помощи DDMS File Explorer

Перейдем непосредственно к реализации чтения/записи во внутренюю память:


public class FileUtils {
public static File EXTERNAL_DIR = Environment.getExternalStorageDirectory();
private FileUtils() {
};
public static boolean writeInternal(Context context, InputStream source, String fileName, int mode) {
BufferedOutputStream dest = null;
try {
dest = new BufferedOutputStream(context.openFileOutput(fileName, mode));
CommonIO.writeFromInputToOutput(source, dest);
return true;
} catch (FileNotFoundException e) {
e.printStackTrace();
return false;
} finally {
CommonIO.close(dest);
}
}
public static InputStream readFromInternalile(Context context, String fileName) {
BufferedInputStream source = null;
try {
source = new BufferedInputStream(context.openFileInput(fileName));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return source;
}
...
//Дальнейшее описание класса будет позже

Как видите, все просто и скупо на код благодаря использованию методов writeFromInputToOutput() и close(), методы просто для понимания, но я отдельно остановлюсь на параметре mode метода writeInternal().

Этот параметр задает разрешения и стратегию работы с фалом и может иметь следующие значиния:

  • MODE_APPEND = 0×00008000 : сообщает о том, что если файл существует, то не нужно затирать содежимое, а дописывать в конец;
  • MODE_PRIVATE = 0×00000000 : говорит о том, что доступ к файлу будет только у приложения, которое его создало;
  • MODE_WORLD_READABLE = 0×00000001 : данной файл смогут читать другие приложения;
  • MODE_WORLD_WRITEABLE = 0×00000002 : данный файл будет доступен для записи другим приложениям;

Все вышеописанные константны явлюят полями класса Context и почитать о них можно на developer.android.com.

Работа с файлами во внешней памяти

У файлов во внешней памяти есть приятная особенность: файлы во внешней памяти могут быть доступны всем приложениям. Для работы с ними применимы следующие методы:


public static File EXTERNAL_DIR = Environment.getExternalStorageDirectory();
//Код пропущен, был описан выше
public static boolean writeExternal(InputStream source, String filePath) {
if (!CommonIO.isExternalAvailable())
return false;

File dest = new File(EXTERNAL_DIR + File.separator + filePath);
FileOutputStream writer = null;
try {
if (!dest.exists()) {
dest.getParentFile().mkdirs();
dest.createNewFile();
}
writer = new FileOutputStream(dest);
CommonIO.writeFromInputToOutput(source, writer);
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
} finally {
CommonIO.close(writer);
}
}
public static InputStream readExternal(String filePath) {
if (!CommonIO.isExternalAvailable())
return null;

File source = new File(EXTERNAL_DIR + File.separator + filePath);
InputStream out = null;
if (source.exists()) {
try {
out = new FileInputStream(source);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
return out;
}

Константное значение EXTERNAL_DIR необходимо нам по той причине, что в зависимости от системы, путь к смонтированному образу может отличаться. Думаю вы опять заметили, от сколькой рутины нас изабавило наличие утилитарных методов.

Остановлюсь на следующем участке кода:


if (!dest.exists()) {
dest.getParentFile().mkdirs();
dest.createNewFile();
}

При создании файла возможна ситуация, когда родительские директории не существуют, чтобы избавится от этой проблемы мы используем dest.getParentFile().mkdirs();.

Итоги

И так, мы введи несколько полезных методов для работы с файлами в Android, оценили полезность создания утилитарных методов. За кадром остался доступ к файлом приложения A из приложения B, возможно это стенет темой следующей статьи.

К заключении хочу отметить, что теперь код большинста примеров с сайта доступен на code.google.com. Надеюсь такое нововведине придется вам по душе :)

Для тех, кто любит архивы iDevTools.

Happy Coding! ;)

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *