Работа с файловой системой
Чтение и сохранение файлов
Работа с настройками уровня activity и приложения позволяет сохранить небольшие данные отдельных типов (string, int), но для работы с большими массивами данных, такими как графически файлы, файлы мультимедиа и т.д., нам придется обращаться к файловой системе.
ОС Android построена на основе Linux. Этот факт находит свое отражение в работе с файлами. Так, в путях к файлам в качестве разграничителя в Linux использует слеш «/», а не обратный слеш «\» (как в Windows). А все названия файлов и каталогов являются регистрозависимыми, то есть «data» это не то же самое, что и «Data».
Приложение Android сохраняет свои данные в каталоге /data/data/ / и, как правило, относительно этого каталога будет идти работа.
Для работы с файлами абстрактный класс android.content.Context определяет ряд методов:
boolean deleteFile (String name) : удаляет определенный файл
String[] fileList () : получает все файлы, которые содержатся в подкаталоге /files в каталоге приложения
File getCacheDir() : получает ссылку на подкаталог cache в каталоге приложения
File getDir(String dirName, int mode) : получает ссылку на подкаталог в каталоге приложения, если такого подкаталога нет, то он создается
File getExternalCacheDir() : получает ссылку на папку /cache внешней файловой системы устройства
File getExternalFilesDir(String type) : получает ссылку на каталог /files внешней файловой системы устройства
File getFileStreamPath(String filename) : возвращает абсолютный путь к файлу в файловой системе
FileInputStream openFileInput(String filename) : открывает файл для чтения
FileOutputStream openFileOutput (String name, int mode) : открывает файл для записи
Все файлы, которые создаются и редактируются в приложении, как правило, хранятся в подкаталоге /files в каталоге приложения.
Для непосредственного чтения и записи файлов применяются также стандартные классы Java из пакета java.io.
Итак, применим функционал чтения-записи файлов в приложении. Пусть у нас будет следующая примитивная разметка layout:
Поле EditText предназначено для ввода текста, а TextView — для вывода ранее сохраненного текста. Для сохранения и восстановления текста добавлены две кнопки.
Теперь в коде Activity пропишем обработчики кнопок с сохранением и чтением файла:
При нажатии на кнопку сохранения будет создаваться поток вывода FileOutputStream fos = openFileOutput(FILE_NAME, MODE_PRIVATE)
В данном случае введенный текст будет сохраняться в файл «content.txt». При этом будет использоваться режим MODE_PRIVATE
Система позволяет создавать файлы с двумя разными режимами:
MODE_PRIVATE : файлы могут быть доступны только владельцу приложения (режим по умолчанию)
MODE_APPEND : данные могут быть добавлены в конец файла
Поэтому в данном случае если файл «content.txt» уже существует, то он будет перезаписан. Если же нам надо было дописать файл, тогда надо было бы использовать режим MODE_APPEND:
Для чтения файла применяется поток ввода FileInputStream :
Подробнее про использование потоков ввода-вывода можно прочитать в руководстве по Java: https://metanit.com/java/tutorial/6.3.php
В итоге после нажатия кнопки сохранения весь текст будет сохранен в файле /data/data/название_пакета/files/content.txt
Где физически находится созданный файл? Чтобы увидеть его на подключенном устройстве перейдем в Android Stud в меню к пункту View -> Tool Windows -> Device File Explorer
После этого откроектся окно Device File Explorer для просмотра файловой системы устройства. И в папке data/data/[название_пакета_приложения]/files мы сможем найти сохраненный файл.
Источник
7. Основы Kotlin. Файловые операции
Программы, которые мы пишем, так или иначе должны взаимодействовать с пользователем и внешней средой — операционной системой, устройствами компьютера, сетью Интернет. Простейшим способом взаимодействия является ввод с консоли и вывод на консоль, но сейчас такой способ применяется крайне редко. Более совершенным способом взаимодействия являются файлы — многие программы берут из них настроечную или входную информацию, и используют их для сохранения результатов своей работы или различных настроек. Например, всевозможные редакторы позволяют открывать файлы в определённом формате, просматривать и/или изменять их, сохранять файлы на диске компьютера или в сети Интернет.
В библиотеке Java внутри пакета java.io имеется ряд типов, обеспечивающих возможность работы с файлами, а библиотека Котлина дополняет их некоторыми удобными возможностями. Как обычно, рассмотрим часть этих возможностей на примере.
Краеугольный тип, используемый для работы с файлами в Котлине — тип java.io.File . В соответствии с названием, он предназначен для различных операций с файлами; объект этого типа соответствует какому-либо реальному файлу, чаще всего находящемуся на жёстком диске. Для создания файла используется специальная функция-конструктор: File(inputName) или File(outputName) в примере. Если в аргументе конструктора указано только имя файла — поиск файла происходит в текущей директории, а если аргумент содержим также путь к файлу — то в директории, указанной этим путём. Специфика конструктора заключается в том, что его имя совпадает с типом объекта, которую он создаёт, и он имеет результат соответствующего типа. Более подробно мы поговорим о конструкторах в следующем уроке.
Обмен данными с файлом может происходить в режиме чтения либо в режиме записи. В режиме чтения информации, заданное имя должно соответствовать уже существующему файлу. Один из способов получения информации из файла — вызов функции file.readLines() . Результат вызова — список строк, из которых состоит файл. Каждый String в этом списке соответствует одной строке файла, строки файла разделяются символом «возврат каретки» и / или «новая строка».
В режиме записи информации, заданное имя может не соответствовать существующему файлу — в этом случае он будет создан. Для записи информации, необходимо создать один из объектов, обеспечивающих такую возможность. В примере, таким объектом является val writer = File(outputName).bufferedWriter() — то есть необходимо вызвать функцию bufferedWriter() на получателе, соответствующем исходному файлу. Как видно из текста примера, writer (писатель) имеет функции writer.newLine() (добавление в файл новой строки), writer.write(string) (добавление в файл заданной строки) и writer.close() (закрытие писателя, выполняется строго ПОСЛЕ выполнения всех остальных действий и фиксирует итоговое состояние файла).
Мы перечислили все файловые операции, присутствующие в исходном примере. Внутри цикла for , каждая из строк файла разбивается по пробелам на слова, с этой целью используется Regex(«\\s+») . В currentLineLength накапливается длина текущей строки ВЫХОДНОГО файла. Если в текущей строке достаточно места для очередного слова ВХОДНОГО файла, слово добавляется в текущую строку, в противном случае в файл добавляется перевод строки и слово добавляется в новую строку. Пустые строки входного файла, как и сказано в задании, переносятся в выходной файл без изменений.
Источник
Kotlin Native: следите за файлами
Когда вы пишите command line утилиту, последнее, на что вам хочется полагаться, так это на то, что на компьютере где она будет запущена установлен JVM, Ruby или Python. Так же хотелось бы на выходе иметь один бинарный файл, который будет легко запустить. И не возиться слишком много с memory management’ом.
По вышеозначенным причинам, в последние годы всегда, когда мне нужно было писать подобные утилиты, я использовал Go.
У Go относительно простой синтаксис, неплохая стандартная библиотека, есть garbage collection, и на выходе мы получаем один бинарник. Казалось бы, что еще нужно?
Не так давно Kotlin так же стал пробовать себя на схожем поприще в форме Kotlin Native. Предложение звучало многообещающе — GC, единый бинарник, знакомый и удобный синтаксис. Но все ли так хорошо, как хотелось бы?
Задача, которую нам предстоит решить: написать на Kotlin Native простой file watcher. Как аргументы утилита должна получать путь к файлу и частоту проверки. Если файл изменился, утилита дожна создать его копию в той же папке с новым именем.
Иначе говоря, алгоритм должен выглядеть следующим образом:
Ладно, с тем чего хотим добиться вроде бы разобрались. Время писать код.
Среда
Первое, что нам потребуется — это IDE. Любителей vim попрошу не беспокоиться.
Запускаем привычный IntelliJ IDEA и обнаруживаем, что в Kotlin Native он не может от слова совсем. Нужно использовать CLion.
На этом злоключения человека, который в последний раз сталкивался с C в 2004 еще не окончены. Нужен toolchain. Если вы используете OSX, скорее всего CLion обнаружит подходящий toolchain сам. Но если вы решили использовать Windows и на C не программируете, придется повозиться с tutorial’ом по установке какого-нибудь Cygwin.
IDE установили, с toolchain’ом разобрались. Можно уже начать код писать? Почти.
Поскольку Kotlin Native еще несколько экспериментален, плагин для него в CLion не установлен по умолчанию. Так что прежде, чем мы увидим заветную надпись «New Kotlin/Native Application» придется его установить вручную.
Немного настроек
И так, наконец-то у нас есть пустой Kotlin Native проект. Что интересно, он основан на Gradle (а не на Makefile’ах), да еще на Kotlin Script версии.
Заглянем в build.gradle.kts :
Единственный плагин, который мы будем использовать называется Konan. Он то и будет производить наш бинарный файл.
В konanArtifacts мы указываем имя исполняемого файла. В данном примере получится file_watcher.kexe
Пора бы уже и код показать. Вот он, кстати:
Обычно у command line утилиты бывают так же опциональные аргументы и их значения по умолчанию. Но для примера будем предполагать, что аргумента всегда два: path и interval
Для тех, кто с Kotlin уже работал можем показаться странным, что path оборачивается в свой собственный класс File , без использования java.io.File . Объяснение этому — черезе минуту-другую.
Если вы вдруг не знакомы с функцией require() в Kotlin — это просто более удобный способ для валидации аргументов. Kotlin — он вообще про удобство. Можно было бы написать и так:
В целом, тут пока обычный Kotlin код, ничего интересного. А вот с этого момента станет повеселей.
Давайте будем пытаться писать обычный Kotlin-код, но каждый раз, когда нам нужно использовать что-нибудь из Java, мы говорим «упс!». Готовы?
Вернемся к нашему while , и пусть он отпечатывает каждый interval какой-нибудь символ, к примеру точку.
Thread — это класс из Java. Мы не можем использовать Java классы в Kotlin Native. Только Kotlin’овские классы. Никакой Java.
Кстати, потому в main мы и не использовали java.io.File
Хорошо, а что тогда можно использовать? Функции из C!
Добро пожаловать в мир C
Теперь, когда мы знаем что нас ждет, давайте посмотрим как выглядит функция exists() из нашего File :
File это простой data class , что дает нам имплементацию toString() из коробки, которой мы потом воспользуемся.
«Под капотом» мы вызываем C функцию access() , которая возвращает -1 , если такого файла не существует.
Дальше по списку у нас функция modified() :
Функцию можно было бы немного упростить используя type inference, но тут я решил этого не делать, чтобы было понятно, что функция не возвращает, к примеру, Boolean .
В этой фукнции есть две интересные детали. Во-первых, мы используем alloc() . Поскольку мы используем C, иногда нужно выделять структуры, а делается это в C вручную, при помощи malloc().
Высвобождать эти структуры тоже нужно вручную. Тут на помощь приходит функция memScoped() из Kotlin Native, которая это сделает за нас.
Нам осталось рассмотреть наиболее увесистую функцию: сopyAside()
Тут мы используем С функцию copyfile_state_alloc() , которая выделяет нужную для copyfile() структуру.
Но и высвобождать нам эту структуру приходится самим — используя
copyfile_state_free(state)
Последнее, что осталось показать — это генерация имен. Тут просто немного Kotlin:
Это довольно наивный код, который игнорирует многие кейсы, но для примера сойдет.
Теперь как все это запускать?
Один вариант — это конечно использовать CLion. Он все сделает за нас.
Но давайте вместо этого используем command line, чтобы лучше понять процесс. Да и какой-нибудь CI не будет запускать наш код из CLion.
Первым делом мы компилируем наш проект используя Gradle. Если все прошло успешно, появится следующее сообщение:
Шестнадцать секунд?! Да, в сравнение с каким-нибудь Go или даже Kotlin для JVM, результат неутешителен. И это еще крошечный проект.
Теперь вы должны увидеть бегущие по экрану точки. И если вы измените содержимое файла, об этом появится сообщение. Что-то вроде такой картины:
Время запуска замерить сложно. Зато мы можем проверить, сколько памяти занимает наш процесс, используя к примеру Activity Monitor: 852KB. Неплохо!
Немного выводов
И так, мы выяснили что при помощи Kotlin Native мы можем получить единый исполняемый файл с memory footprint’ом меньше, чем у того же Go. Победа? Не совсем.
Как это все тестировать? Те кто работал с Go или Kotlin’ом знаю, что в обоих языках есть хорошие решения для этой важной задачи. У Kotlin Native с этим пока что все плохо.
Вроде бы в 2017ом JetBrains пытались это решить. Но учитывая, что даже у официальных примеров Kotlin Native нет тестов, видимо пока не слишком успешно.
Другая проблема — crossplatform разработка. Те, кто работали с C побольше моего уже наверняка заметили, что мой пример будет работать на OSX, но не на Windows, поскольку я полагаюсь на несколько функций доступных только с platform.darwin . Надеюсь что в будущем у Kotlin Native появится больше оберток, которые позволят абстрагироваться от платформы, к примеру при работе с файловой системой.
Все примеры кода вы можете найти тут
И ссылка на мою оригинальную статью, если вы предпочитаете читать на английском
Источник
Котлин: Как создать / добавить файл (и поделиться)
У меня есть список массивов данных в моем приложении, которые я бы сейчас хотел записать в файл (csv) и использовать стороннее приложение (например, электронную почту), чтобы поделиться этим файлом csv. Мне не повезло найти полезные ресурсы для создания, поиска пути к файлу и добавления файла в Kotlin. У кого-нибудь есть опыт с этим или есть примеры, на которые можно указать? Просто для начала я пытаюсь написать заголовок и закрыть файл, чтобы я мог видеть, что он пишет правильно.
Это то, что я имею сейчас:
Это не вызывает исключения, но я не могу найти файл в файловой системе. Я использую физический планшет для тестирования / отладки. Я проверил папку com . для моего приложения.
3 ответа
Похоже, существует множество способов создать файл и добавить его в зависимости от минимальной версии API, для которой вы разрабатываете. Я использую минимальный Android API 22. Код для создания / добавления файла ниже:
Я не могу найти файл в файловой системе
Используйте Обозреватель файлов устройств Android Studio и найдите /data/data/. /files/ , где . — идентификатор вашего приложения.
Кроме того, вы можете написать свой код более кратко, как:
use() автоматически закроет PrintWriter , а PrintWriter даст вам более естественный API для записи текста.
openFileOutput() создает личный файл, вероятно, внутри хранилища приложения. Эти файлы не доступны для просмотра по умолчанию. Если вы хотите создать файл, к которому можно просматривать, вам понадобится разрешение WRITE_EXTERNAL_STORAGE, и вы захотите создать файлы в каталог, такой как предоставляется getExternalFilesDir()
Источник