Содержание
В этой статье я опишу работу с файлами на 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!
Сообщить об опечатке
Текст, который будет отправлен нашим редакторам: