Я решил написать еще одну статью об элементарных для опытного разработчика вещах, но вызывающих проблемы у новичков. Если погуглить вопрос как прочитать файл, то в основном попадуться старые статьи с советом, что Вам нужно запросить разрешение.

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)


  1. kovserg
    22.10.2023 14:30
    +6

    В андроиде нет файлов

    Есть там файлы, просто вам доступ к ним постепенно перекрывают. Для вашей же безопасности.
    В результате придётся делать один большой файл который будет образом диска с файлами и спокойно к ним обращаться без участия android-а. И тогда можно будет передавать uri на образ диска и работать с файлами как обычно. Более того почему в android нет поддержки сетевых дисков и программы типа vlc сами реализуют этот функционал?


    1. durnoy
      22.10.2023 14:30
      +1

      "Сетевой диск" может значить несколько разных вещей. Конечно, по умолчанию можно считать Windows и протокол SMB. Но ещё есть NFS в *nix, и у Apple какое-то время был свой протокол AFP.

      Все эти протоколы изначально не особо рассчитаны на мобильные устройства, которые могут прыгать из сети в сеть.

      Легко могу представить, что андроид не хочет поддерживать зоопарк протоколов, у которых куча разных версий.

      Пользователь, на телефоне открывающий файл с домашнего файлового сервера -- очень узкий случай в массе всех пользователей телефонов, кмк.


  1. beDenz
    22.10.2023 14:30
    +1

    Может кто подскажет. В последних версиях андроида закрыли доступ к буферу обмена в webview. Не даёт возможность скопировать какой либо текст. Разработчик разводит руками.


    1. DaemonGloom
      22.10.2023 14:30
      +1

      Разрешения на работу с буфером запросили? Вызвать нативный метод пробовали?


      1. beDenz
        22.10.2023 14:30

        Да. Разрешения задавали, не помогло. За нативный метод спасибо. Не знал, что можно в webview его так легко пробросить.


    1. 402d Автор
      22.10.2023 14:30
      +2

      да уже года два как целый класс приложений умер. Всяческие менеджеры буфера обмена. Порезали доступ к возможности до получить только последнее и только если твоя активити имеет фокус на редактируемом элементе.

      Полноценный доступ к буферу обмена оставили только для софт клавиатур и то только в случае, если пользователь сделал ее методом ввода по умолчанию.


  1. izhXX
    22.10.2023 14:30
    +1

    Слишком громкий заголовок для статьи :(

    Ожидал увидеть здесь создание файла и доступ к нему извне, но нашёл лишь пересказ документации. Статья хорошая, однако не описаны все возможности создания файла без запроса пермишена и в обход файлопикера. Тут и использование /obb, /media должно быть, но увы.


    1. 402d Автор
      22.10.2023 14:30
      +3

      Так в чем проблема. Напишите тоже. Я вот самые основы осветил. Просто на 4 пда человек жаловался, что модерация на его программу за доступ ко всем файлам реджектнула. На тостере опять набег вопрошающих был. А по сути не всем доступ то нужен. Можно часть задач решить и элементарными методами. Просто чуть перестроить восприятие файла. Никак я должен сам лезть и писать в нужное место, а эй кто там умеет файлы записывать ? Ура. Ну так ловите данные.

      Ситуация в андроиде все хуже и хуже. Загоняют в песочницу, из которой остается только право что-то показать на экране. :(


      1. izhXX
        22.10.2023 14:30

        Плох тот Android-разраб, который бежит на форум после реджекта от ГП :D

        По существу то всё так, но песочница это не всегда плохо (см. ситуацию с доступом ко всем файлам), особенно со стороны юзера. Ну а писать статью о том, что можно найти без особых усилий даже джуну - ну не знаю. Обычно это всё решается докой + небольшими проверками кода. Да и там материала на пару строчек в итоге, совсем всё просто.


  1. Keksik222
    22.10.2023 14:30

    Вот бы был способ, чтобы можно было прописать приложению, полный доступ к папкам data, obb, без Root, adb и разрешения системного файлпикера, на Android 13+


    1. ohno1052
      22.10.2023 14:30

      доступ к obb по дефолту есть, и к собственной папочке в data. Разрешение для папочек других приложений в data можно выдать через тот самый системный fm. Больше - рут извольте.


      1. Keksik222
        22.10.2023 14:30

        Вот об этом, я и говорил. Чтобы разрешение для папок других приложений, можно было получить, не только через ROOT или системный файлпикер. Хочу вообще сломать scoped storage на Android 13, но к сожалению, пока безуспешно.


    1. izhXX
      22.10.2023 14:30

      Из коробки приложение пишет в свою папку в data, obb и media. У юзверя есть доступ к media и obb. Собственно, внутри аппки без запроса пермишена можно потыкаться в obb и media точно, data под вопросом.


      1. Keksik222
        22.10.2023 14:30

        Всё что я нашёл, дак это то что получить через стороннее можно только через рут или выдавая вручную на каждую папку, через файлы. Пытался получить на 13 к чужим obb, ошибка. Выдавать вручную через системный. Или рут.


        1. izhXX
          22.10.2023 14:30

          Через системный FP вы никогда не дадите доступ к data&obb. Но, при наличии папок и знании package обходными путями можно получить доступ к файлам или папкам внутри media (это точно), obb (50/50, чтение вроде можно). Пример с доступом к media. Это если мы говорим про разработку, а не использование. В использовании всё чуть по другому.