Я решил написать еще одну статью об элементарных для опытного разработчика вещах, но вызывающих проблемы у новичков. Если погуглить вопрос как прочитать файл, то в основном попадуться старые статьи с советом, что Вам нужно запросить разрешение.
Manifest.permission.READ_EXTERNAL_STORAGE
Этот подход нормально работал до появления Андроид 11. Потом с помощью специального костыля можно было какое то время жить по старому.
Совсем упертые могут попробывать доказать модерации, что Вам нужен
android.permission.MANAGE_EXTERNAL_STORAGE
Для тех кому сразу интересен результат https://github.com/Muraveiko/EditorExample/blob/main/app/src/main/java/ru/a402d/verysimpleeditor/MainActivity.java#L73 для остальных продолжу по шагам.
Ребят я скажу только одну фразу, после которой все станет на свои места:
В андроиде нет файлов.
Загорелось меня поправить ? Готовы привести кучу примеров почему я не прав. Подождите. Поясню.
1. Забудьте о том как это работает в Windows/Unix (имя и путь к файлу). Фактически нам не нужно для чтения данных фактическое месторасположение файла и его наименование.
static String readTxtFile(InputStream inputStream) {
BufferedReader br = null;
StringBuilder sb = new StringBuilder();
String line;
try {
br = new BufferedReader(new InputStreamReader(inputStream));
while ((line = br.readLine()) != null) {
sb.append(line).append("\n");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return sb.toString();
}
Данные мы читаем из потока, который получаем:
getContentResolver().openInputStream(p)
где p - Uri - Универсальный идентификатор ресурса. Файл - это частный случай uri.
2. В адроиде отказались от схемы file: . Взамен ее используется content:
При взаимодействие программ между собой в андроиде используются контент провадеры.
Современный подход получить нужный uri очень просто:
mGetContent.launch("*/*");
Предварительно мы обявляем активитилаунчер и используем готовый хелпер для нужного действия, и указание на метод обработки полученного результата:
private final ActivityResultLauncher<String> mGetContent =
registerForActivityResult(
new ActivityResultContracts.GetContent(),
this::fileSelected
);
Это рекомендуемая сейчас замена для activityStartForResult (депрекейтед).
Таким образом для чтения любого файла нам не потребавалось никаких дополнительных разрешений. Доступ к файлу нам предоставил файловый менеджер смартфона.
После неявного вызова ACTION_GET_CONTENT, мы получили URI контента вместе с разрешением его прочитать.
Аналогично для записи:
private final ActivityResultLauncher<Intent> mSaveContent =
registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
this::saveSelected
);
только нужный интент проще собрать ручками:
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_TITLE, cTime+".txt");
На гитхабе в качестве примера выложен Очень Простой Редактор :)
Комментарии (15)
beDenz
22.10.2023 14:30+1Может кто подскажет. В последних версиях андроида закрыли доступ к буферу обмена в webview. Не даёт возможность скопировать какой либо текст. Разработчик разводит руками.
DaemonGloom
22.10.2023 14:30+1Разрешения на работу с буфером запросили? Вызвать нативный метод пробовали?
beDenz
22.10.2023 14:30Да. Разрешения задавали, не помогло. За нативный метод спасибо. Не знал, что можно в webview его так легко пробросить.
402d Автор
22.10.2023 14:30+2да уже года два как целый класс приложений умер. Всяческие менеджеры буфера обмена. Порезали доступ к возможности до получить только последнее и только если твоя активити имеет фокус на редактируемом элементе.
Полноценный доступ к буферу обмена оставили только для софт клавиатур и то только в случае, если пользователь сделал ее методом ввода по умолчанию.
izhXX
22.10.2023 14:30+1Слишком громкий заголовок для статьи :(
Ожидал увидеть здесь создание файла и доступ к нему извне, но нашёл лишь пересказ документации. Статья хорошая, однако не описаны все возможности создания файла без запроса пермишена и в обход файлопикера. Тут и использование /obb, /media должно быть, но увы.
402d Автор
22.10.2023 14:30+3Так в чем проблема. Напишите тоже. Я вот самые основы осветил. Просто на 4 пда человек жаловался, что модерация на его программу за доступ ко всем файлам реджектнула. На тостере опять набег вопрошающих был. А по сути не всем доступ то нужен. Можно часть задач решить и элементарными методами. Просто чуть перестроить восприятие файла. Никак я должен сам лезть и писать в нужное место, а эй кто там умеет файлы записывать ? Ура. Ну так ловите данные.
Ситуация в андроиде все хуже и хуже. Загоняют в песочницу, из которой остается только право что-то показать на экране. :(
izhXX
22.10.2023 14:30Плох тот Android-разраб, который бежит на форум после реджекта от ГП :D
По существу то всё так, но песочница это не всегда плохо (см. ситуацию с доступом ко всем файлам), особенно со стороны юзера. Ну а писать статью о том, что можно найти без особых усилий даже джуну - ну не знаю. Обычно это всё решается докой + небольшими проверками кода. Да и там материала на пару строчек в итоге, совсем всё просто.
Keksik222
22.10.2023 14:30Вот бы был способ, чтобы можно было прописать приложению, полный доступ к папкам data, obb, без Root, adb и разрешения системного файлпикера, на Android 13+
ohno1052
22.10.2023 14:30доступ к obb по дефолту есть, и к собственной папочке в data. Разрешение для папочек других приложений в data можно выдать через тот самый системный fm. Больше - рут извольте.
Keksik222
22.10.2023 14:30Вот об этом, я и говорил. Чтобы разрешение для папок других приложений, можно было получить, не только через ROOT или системный файлпикер. Хочу вообще сломать scoped storage на Android 13, но к сожалению, пока безуспешно.
izhXX
22.10.2023 14:30Из коробки приложение пишет в свою папку в data, obb и media. У юзверя есть доступ к media и obb. Собственно, внутри аппки без запроса пермишена можно потыкаться в obb и media точно, data под вопросом.
Keksik222
22.10.2023 14:30Всё что я нашёл, дак это то что получить через стороннее можно только через рут или выдавая вручную на каждую папку, через файлы. Пытался получить на 13 к чужим obb, ошибка. Выдавать вручную через системный. Или рут.
izhXX
22.10.2023 14:30Через системный FP вы никогда не дадите доступ к data&obb. Но, при наличии папок и знании package обходными путями можно получить доступ к файлам или папкам внутри media (это точно), obb (50/50, чтение вроде можно). Пример с доступом к media. Это если мы говорим про разработку, а не использование. В использовании всё чуть по другому.
kovserg
Есть там файлы, просто вам доступ к ним постепенно перекрывают. Для вашей же безопасности.
В результате придётся делать один большой файл который будет образом диска с файлами и спокойно к ним обращаться без участия android-а. И тогда можно будет передавать uri на образ диска и работать с файлами как обычно. Более того почему в android нет поддержки сетевых дисков и программы типа vlc сами реализуют этот функционал?
durnoy
"Сетевой диск" может значить несколько разных вещей. Конечно, по умолчанию можно считать Windows и протокол SMB. Но ещё есть NFS в *nix, и у Apple какое-то время был свой протокол AFP.
Все эти протоколы изначально не особо рассчитаны на мобильные устройства, которые могут прыгать из сети в сеть.
Легко могу представить, что андроид не хочет поддерживать зоопарк протоколов, у которых куча разных версий.
Пользователь, на телефоне открывающий файл с домашнего файлового сервера -- очень узкий случай в массе всех пользователей телефонов, кмк.