Обновление на Android Lollipop и особенности работы с внешней картой памяти. Получение пути к карте памяти SD Card на Android

01.05.2019

Проблема

Сообщение направляет вас посетить два веб-сайта:

Это выглядит как внутренний пример (возможно, позже будет показан в демонстрациях API), но довольно сложно понять, что происходит.

Это официальная документация по новому API, но в нем недостаточно информации о том, как ее использовать.

Вот что он вам говорит:

Если вам действительно нужен полный доступ ко всему поддереву документов, начните с запуска ACTION_OPEN_DOCUMENT_TREE, чтобы пользователь мог выбрать каталог. Затем передайте полученный getData() в fromTreeUri (Context, Uri), чтобы начать работу с выбранным пользователем деревом.

При навигации по дереву экземпляров DocumentFile вы всегда можете использовать getUri(), чтобы получить Uri, представляющий базовый документ для этот объект для использования с openInputStream (Uri) и т.д.

Чтобы упростить код на устройствах, работающих под управлением KITKAT или ранее, вы можете используйте fromFile (Файл), который эмулирует поведение DocumentProvider.

Вопросы

У меня есть несколько вопросов о новом API:

2 ответов

Множество хороших вопросов, дайте понять.:)

Как вы его используете?

Здесь отличный учебник для взаимодействия с платформой доступа к хранилищу в KitKat:

Взаимодействие с новыми API в Lollipop очень похоже. Чтобы предложить пользователю выбрать дерево каталогов, вы можете запустить такое намерение следующим образом:

Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); startActivityForResult(intent, 42);

Затем в вашем onActivityResult() вы можете передать выбранный пользователем Uri в новый вспомогательный класс DocumentFile. Вот краткий пример, в котором перечислены файлы в выбранном каталоге, а затем создается новый файл:

Public void onActivityResult(int requestCode, int resultCode, Intent resultData) { if (resultCode == RESULT_OK) { Uri treeUri = resultData.getData(); DocumentFile pickedDir = DocumentFile.fromTreeUri(this, treeUri); // List all existing files inside picked directory for (DocumentFile file: pickedDir.listFiles()) { Log.d(TAG, "Found file " + file.getName() + " with size " + file.length()); } // Create a new file and write into it DocumentFile newFile = pickedDir.createFile("text/plain", "My Novel"); OutputStream out = getContentResolver().openOutputStream(newFile.getUri()); out.write("A long time ago...".getBytes()); out.close(); } }

Если вы хотите получить доступ к этому Uri из собственного кода, вы можете вызвать ContentResolver.openFileDescriptor() , а затем использовать ParcelFileDescriptor.getFd() или detachFd() для получения традиционного целочисленного дескриптора файла POSIX.

Как вы можете проверить доступ к файлам/папкам?

По умолчанию Uris, возвращаемый с помощью возможностей Storage Access Framework, не сохраняется при перезагрузках. Платформа "предлагает" возможность сохранять разрешение, но вам все равно нужно "взять" разрешение, если вы этого хотите. В нашем примере выше вы бы назвали:

GetContentResolver().takePersistableUriPermission(treeUri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

Вы всегда можете выяснить, какие постоянные гранты для вашего приложения доступны через API ContentResolver.getPersistedUriPermissions() . Если вам больше не нужен доступ к сохраненному Uri, вы можете освободить его с помощью ContentResolver.releasePersistableUriPermission() .

Доступно ли это на KitKat?

Нет, мы не можем ретроактивно добавлять новые функции к более старым версиям платформы.

Могу ли я увидеть, какие приложения имеют доступ к файлам/папкам?

В настоящее время нет пользовательского интерфейса, который показывает это, но вы можете найти информацию в разделе "Предоставленные разрешения Uri" вывода adb shell dumpsys activity providers .

Что произойдет, если приложение установлено для нескольких пользователей на одном устройстве?

Разрешения на разрешение Uri изолированы для каждого пользователя, как и все другие функции многопользовательской платформы. То есть, одно и то же приложение, работающее под двумя разными пользователями, не имеет наложенных или общих разрешений Uri.

Можно ли отозвать разрешения?

Подпрограмма DocumentProvider может аннулировать разрешение в любое время, например, когда удаляется облачный документ. Наиболее распространенный способ обнаружения этих отозванных разрешений - это когда они исчезают из ContentResolver.getPersistedUriPermissions() , упомянутых выше.

Разрешения также отменяется всякий раз, когда данные приложения очищаются для любого приложения, участвующего в гранте.

Будет ли запрос рекурсивно работать в выбранной папке?

Yep, намерение ACTION_OPEN_DOCUMENT_TREE дает вам рекурсивный доступ к существующим и вновь созданным файлам и каталогам.

Предоставляет ли это несколько вариантов?

Да, с KitKat поддерживался множественный выбор, и вы можете разрешить его, установив EXTRA_ALLOW_MULTIPLE при запуске вашего намерения ACTION_OPEN_DOCUMENT . Вы можете использовать Intent.setType() или EXTRA_MIME_TYPES , чтобы сузить типы файлов, которые можно выбрать:

Есть ли способ эмулятора попробовать новый API?

Да, основное устройство хранения данных должно появиться в сборщике, даже на эмуляторе. Если ваше приложение использует платформу доступа к хранилищу для доступа к разделяемому хранилищу, вам больше не нужны разрешения READ/WRITE_EXTERNAL_STORAGE и вы можете удалить их или использовать функцию android:maxSdkVersion , чтобы запрашивать их только на более ранних версиях платформы.

Что происходит, когда пользователь заменяет SD-карту другим?

Когда задействуются физические носители, UUID (такой как серийный номер FAT) основного носителя всегда записывается в возвращаемый Uri. Система использует это для подключения к мультимедиа, который был выбран пользователем, даже если пользователь меняет носитель между несколькими слотами.

Если пользователь поменяется на второй карте, вам необходимо запросить доступ к новой карте. Поскольку система запоминает гранты на основе UUID, вы будете продолжать иметь ранее предоставленный доступ к исходной карте, если пользователь повторно вставляет ее позже.

В моем проекте Android в Github, приведенном ниже, вы можете найти рабочий код, который позволяет писать на extSdCard на Android 5. Предполагается, что пользователь дает доступ ко всей SD-карте, а затем позволяет писать всюду на этой карте. (Если вы хотите иметь доступ только к одиночным файлам, все становится проще.)

Snipplets главного кода

Запуск платформы доступа к хранилищу:

@TargetApi(Build.VERSION_CODES.LOLLIPOP) private void triggerStorageAccessFramework() { Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); startActivityForResult(intent, REQUEST_CODE_STORAGE_ACCESS); }

Обработка ответа из платформы доступа к хранилищу:

@TargetApi(Build.VERSION_CODES.LOLLIPOP) @Override public final void onActivityResult(final int requestCode, final int resultCode, final Intent resultData) { if (requestCode == SettingsFragment.REQUEST_CODE_STORAGE_ACCESS) { Uri treeUri = null; if (resultCode == Activity.RESULT_OK) { // Get Uri from Storage Access Framework. treeUri = resultData.getData(); // Persist URI in shared preference so that you can use it later. // Use your own framework here instead of PreferenceUtil. PreferenceUtil.setSharedPreferenceUri(R.string.key_internal_uri_extsdcard, treeUri); // Persist access permissions. final int takeFlags = resultData.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); getActivity().getContentResolver().takePersistableUriPermission(treeUri, takeFlags); } } }

Получение выходного потока для файла через платформу доступа к хранилищу (с использованием сохраненного URL-адреса, предполагая, что это URL-адрес корневой папки внешней SD-карты)

DocumentFile targetDocument = getDocumentFile(file, false); OutputStream outStream = Application.getAppContext(). getContentResolver().openOutputStream(targetDocument.getUri());

Используются следующие вспомогательные методы:

Public static DocumentFile getDocumentFile(final File file, final boolean isDirectory) { String baseFolder = getExtSdCardFolder(file); if (baseFolder == null) { return null; } String relativePath = null; try { String fullPath = file.getCanonicalPath(); relativePath = fullPath.substring(baseFolder.length() + 1); } catch (IOException e) { return null; } Uri treeUri = PreferenceUtil.getSharedPreferenceUri(R.string.key_internal_uri_extsdcard); if (treeUri == null) { return null; } // start with root of SD card and then parse through document tree. DocumentFile document = DocumentFile.fromTreeUri(Application.getAppContext(), treeUri); String parts = relativePath.split("\\/"); for (int i = 0; i < parts.length; i++) { DocumentFile nextDocument = document.findFile(parts[i]); if (nextDocument == null) { if ((i < parts.length - 1) || isDirectory) { nextDocument = document.createDirectory(parts[i]); } else { nextDocument = document.createFile("image", parts[i]); } } document = nextDocument; } return document; } public static String getExtSdCardFolder(final File file) { String extSdPaths = getExtSdCardPaths(); try { for (int i = 0; i < extSdPaths.length; i++) { if (file.getCanonicalPath().startsWith(extSdPaths[i])) { return extSdPaths[i]; } } } catch (IOException e) { return null; } return null; } /** * Get a list of external SD card paths. (Kitkat or higher.) * * @return A list of external SD card paths. */ @TargetApi(Build.VERSION_CODES.KITKAT) private static String getExtSdCardPaths() { List paths = new ArrayList<>(); for (File file: Application.getAppContext().getExternalFilesDirs("external")) { if (file != null && !file.equals(Application.getAppContext().getExternalFilesDir("external"))) { int index = file.getAbsolutePath().lastIndexOf("/Android/data"); if (index < 0) { Log.w(Application.TAG, "Unexpected external file dir: " + file.getAbsolutePath()); } else { String path = file.getAbsolutePath().substring(0, index); try { path = new File(path).getCanonicalPath(); } catch (IOException e) { // Keep non-canonical path. } paths.add(path); } } } return paths.toArray(new String); } /** * Retrieve the application context. * * @return The (statically stored) application context */ public static Context getAppContext() { return Application.mApplication.getApplicationContext(); }

Свершилось! На планшет, а именно, на Asus MeMO Pad 7, который я на замену своему честному трудяге, прилетело обновление до Android 5.0.1. Теперь у меня есть устройство с Леденцом, или, как пишут в интернете, с Лолипопой.


Обнова прилетела несколько неожиданно. Если честно, я ждал в апреле новую прошивку на телефон (Asus ZenFone 5) - ее, по крайней мере, обещали . Про планшет же никто ничего не говорил и тут - на тебе, получите и распишитесь.

Это, на самом деле, обескуражило меня (в хорошем, конечно, смысле), еще и потому, что планшет я приобрел в декабре, и на нем стояла версия 4.3. То есть, это второй существенный апдейт операционной системы (на самом деле, обнов было больше, но номер версии Android не менялся). Предыдущий производитель моих устройств - Samsung - не мог похвастаться такой скорострельностью, хотя, тоже дважды обновлял операционку: с Android 3.2 до Android 4.1.2 (через 4.0.1). Только вот ждать этих новых версий приходилось значительно дольше.

Что ж, да здравствуют сюрпризы! Получив уведомление о выходе новой версии прошивки, я тут же скачал ее, а это больше 700 метров, и запустил обновление. Так как качал я не через домашнюю сеть, а через мобильную, то времени на все про все потребовалось чуть больше: от начала загрузки до завершения обновления прошло около часа. Само обновление длилось где-то минут двадцать, но, потенциально, может занимать и больше времени - потому, что после установки операционки следует процесс обновления установленных программ. У меня счетчик досчитал до 205. Но вот, процесс завершился и настало время посмотреть, что же изменилось.

Первое, что бросилось в глаза - более утонченная графика при разблокировке устройства. Второе - рука в области уведомлений. Пару дней я ее терпел, надеясь на то, что случайно где-нибудь увижу что-нибудь, связанное с ней. Не увидел. Тогда стал искать целенаправленно и нашел разъяснение по ней на 4pda . Оказывается, оповещения поделили на важные и не очень, и есть возможность указать, какие оповещения вы хотите получать. Если выставить опцию Оповещать всегда , то рука пропадает, если опцию Только важные оповещения , то рука появляется. Если честно, руку я убирал несколько раз, но, почему-то, она стабильно возвращается. Почему? Пока не знаю.

Вот, собственно, сама процедура по "убиранию" этой руки:

Метод, приведенный выше, совершенно не сложен. Но можно добиться результата еще быстрее и проще: нажать на какую-то из двух кнопок управления громкостью - на экране появится окошко, позволяющее изменять уровни громкости для различных компонентов системы, а также, внимание, управлять Режимами оповещения . Правда, почему-то, эти самые Режимы оповещения выводятся в таком быстром варианте не всегда. Закономерности я пока не нашел, но, если честно, не больно-то и искал.

Несколько слов об установленных программах. Хотя перед началом процесса обновления у меня высветилось предупреждение о том, что некоторые программы будут удалены, так как не входят в новую прошивку, тем не менее, большинство программ сохранилось.


Единственное, чего мне немного жаль, это программа с нетипичным названием "Два приложения". Хотя, признаться, я даже не сразу понял, о чем речь: думал, какие два приложения будут удалены, неужели названия нельзя было указать.

Да, программа, появившаяся в версии 4.4.2 и позволявшая поделить экран между двумя одновременно работающими приложениями, канула в небытие. По правде говоря, ее функционал был довольно слабоват, так как можно было запускать одновременно весьма ограниченный круг приложений. Если мне не изменяет память, я пользовался ею всего один или два раза, просто, чтобы посмотреть, что это такое. Может быть, кому-то будет не хватать возможностей, предоставляемых этой программой, но я, как-то, не особенно расстроился. Хотя, конечно, не очень люблю, когда у меня забирают то, что раньше дали.

Еще парочку замечаний по поводу программ. После того, как я перебрался на новый планшет, я какое-то время устанавливал на нем программы, которыми пользовался на предыдущем своем устройстве. Некоторые из них приходилось , но речь сейчас не о них. Речь о тех программах, в поведении которых что-то изменилось. Конечно, я успел попробовать еще не все, но вот, что обнаружилось на текущий момент.

На KitKat-е у меня решительно не работал TuniIn Radio Pro , купленный, между прочим. Все шло гладко - до того момента, пока очередь не доходила непосредственно до прослушивания какой-либо радиостанции. То есть, программа запускалась, я выбирал группу радиостанций, например, Локальные радиостанции, выбирал конкретную станцию (например, RockFM, Радио Джаз, да вообще, любую станцию) после чего программа валилась с ошибкой. Иногда даже появлялся звук, но программа все равно падала. Время шло, выходили обновления TuneIn Radio, но ничего не менялось. Однако, после обновления до пятерки, проблемы волшебным образом исчезли, теперь все работает без малейших нареканий.

Еще одно положительное изменение - офисный пакет SoftMaker Office HD , тоже честно приобретенный. Программы, входящие в него, нормально работали на самсунговском планшете. Когда я установил эти программы на новый планшет, они вообще не запускались. Через некоторое время вышло обновление для этого офиса и положение несколько выправилось: программы стали запускаться, правда, в процессе работы, в области меню (верхняя строка программного окна), накапливались какие-то странные графические артефакты. При переключении на рабочий стол (или на другое приложение) и обратно артефакты пропадали, но ненадолго. После обновления Android-а все программы, входящие в пакет, работают, как часы.

Ну и небольшой отрицательный момент. Я пользуюсь, очень активно, программкой под названием Видеорегистратор . За нее тоже я тоже деньги отдал. Программа нормально работала на новом планшете, никаких нареканий, плюс, работали функции, которые были недоступны на предыдущем планшете, например, стабилизация видео. После обновления ОС программа при старте записи стала стабильно падать. Поначалу, я думал, что возникли какие-то проблемы с записью на карту памяти - у меня настроена запись в каталог программы на внешней MicroSD карточке. Но, после того, как я в самой программе смог создать подкаталог в каталоге записи, стало понятно, что использование внешней карточки ни при чем. Тогда я решил отключать по одной функции программы, и первое, чего коснулся палец, был checkbox с галкой стабилизации. После выключения опции программа стала писать видео, но его качество упало. Нет, разрешение и скорость потока - все осталось прежним, но картинка на записи поплыла волнами. В программе есть настройка, которая пытается сгладить этот эффект, и, в принципе, ее включение несколько помогает, но, почему то, при повторном входе в программу, сама отметка, что опция включена, остается, но картинка плывет, как-будто опция выключена. Если тыкнуть в уже включенную опцию, все сразу нормализуется. Скорее всего, это ошибка в программе, я собираюсь сообщить о ней разработчику и, возможно, автор исправит этот глюк. Но вот то, что не работает видеостабилизация, немного напрягает. В первую очередь, потому, что штатная программа камеры имеет в настройках опцию стабилизации и, при ее включении, запись ведется без ошибок.

От частных случаев, связанных с программами, перейду к более общим свойствам системы. Ну, во-первых, возросло количество свободной памяти. Как я уже писал, весь мой софт остался на месте. При этом, должен сказать, что я довольно сильно напрягаю планшет. Не в плане того, что постоянно гоняю в игры, а в плане того, что у меня висит масса сервисов, частью которых я, может, и не пользуюсь, но держу на всякий случай. Речь, прежде всего, идет о всевозможных почтовиках - Outlook , Yandex Mail , GMail , точнее, сейчас уже Inbox . Далее, разнообразные облачные хранилища - Yandex Disk , OneDrive , Google Drive , Dropbox , Box , Asus WebStorage . Парочка социальных сетей - Одноклассники и Google+ . Еще всяческие мессенджеры - Viber , ICQ , Skype , Hangouts . Все эти программы, так или иначе, отжирают память устройства, даже когда ими не пользуешься. Но я, в принципе, осознаю, на что иду.

Пользователи асусовских устройств знают, что в шторке Android-а есть приложение, которое позволяет очистить память, хотя бы, на время. Называется оно в пятерке Очистка, а в KitKat звалось более причудливо - Усиление. При открытии шторки иконка этого приложения показывает количество свободной на данный момент памяти. Так вот, когда на планшете была установлена версия Android 4.4.2, цифра эта колебалась в диапазоне 200-300 Мб и иконка, чаще всего, была желтой. Сейчас же количество свободной памяти изменяется от 400 до 600 Мб и иконка практически все время зеленая. Наверное, это изменение связано со сменой виртуальной машины, используемой в Lollipop, но, возможно, что-то изменилось в самой прошивке, например, Asus, что-нибудь убрал. В общем, надо разбираться.

Смена виртуалки должна была повлиять еще на два параметра - быстродействие и энергопотребление. Что могу сказать про быстродействие. Да ничего, наверное. В игрухи я не гоняю, мерятся попугаями (Antutu и прочее) не очень люблю. Планшет, сам по себе, довольно мощный, поэтому я тормозов не замечал ни на KitKat, ни сейчас, в Lollipop. Все плавно и без нареканий. У меня, когда я за рулем, работает Видеорегистратор, Стрелка, Карты (либо Яндекс, либо Гугл), еще я слушаю музыку с помощью Kodi, и, после всего этого, он еще реагирует на переключения на рабочий стол, запуск каких-нибудь других программ, в общем - зверь.

А про энергопотребление... В принципе, последний update на KitKat-е лишил меня возможности жаловаться на неэффективность устройства. Если после первичной установки KitKat планшет за ночь терял по 20-30 процентов энергозапасов, то после обновления до 31-ой версии прошивки потери стали вполне приемлемыми - от двух до пяти процентов за ночь. Обновление до Lollipop ничего не ухудшило в этом смысле, и на том большое спасибо. Что же касается потребления под нагрузкой, тут опять могу привести опыт эксплуатации в автомобиле: все те же исходные - Видеорегистратор, Стрелка, Яндекс Навигатор, Kodi, яркость экрана где-то процентов 75-80 - яркий, солнечный день, плюс все беспроводные интерфейсы включены, так вот, за время поездки - около двух часов - батарея разрядилась до 57 процентов. На мой взгляд, вполне достойно. Кстати, под KitKat было примерно так же.

Ну и последнее - по очереди перечисления, но не по степени важности - что хотелось бы отметить. Речь пойдет о работе с внешней картой памяти. Я , как я выходил из положения, обусловленного ограничениями KitKat на запись на внешнюю карту памяти. Видимо, вал негатива был настолько силен, что Google пришлось прислушаться и что-то изменить. А может они просто изменили что-то, ни к кому не прислушиваясь. Но оставим этот важный вопрос в стороне (прислушались / не прислушались), перейдем к самим изменениям.

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

Первое - программа должна использовать новые возможности Storage Access Framework , а именно, использовать интент OPEN_DOCUMENT_TREE и несколько новых, или обновленных, функций SDK.

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

Третье - удача. У меня на планшете все заработало, как надо, у товарища на HTC One M8 - не заработало, хотя в сети есть видео , как именно на этой модели смартфона все замечательно пашет. Есть предположение, правда, что не заработало по причине того, что карточка в его телефоне была отформатирована как-то не так, может, без указания имени тома, а может, не в той файловой системе.

На самом деле, это и радостная и грустная весть одновременно. Радостная - потому, что теперь не надо получать root доступ к устройству и можно не ограничиваться только каталогом приложения. Грустная - потому, что пока разработчики не соизволят воспользоваться новым API и не выпустят новые версии своих программ, ничего работать не будет. Из моего опыта: файловый менеджер ES Проводник уже обзавелся поддержкой нового API, и у меня получилось дать ему права на запись на всю внешнюю SD карту, а файловый менеджер Total Commander - не обзавелся и по прежнему не умеет писать на MicroSD карточку - никуда, кроме своего собственного каталога. Вот так.

На самом деле, во всей этой истории с записью на внешнюю карту памяти, довольно много нюансов. Например, дали вы каким-то программам доступ, а каким - забыли. Где посмотреть? Я так понимаю, что, в настоящее время - нигде. То есть, в принципе, посмотреть можно, но не на самом планшете, а на компьютере, если подключить к нему планшет и воспользоваться программой adb. Вот нужная команда:

Adb shell dumpsys activity providers

Но как тоже самое получить на планшете - я не нашел.

Следующий вопрос. Вот дали вы какому-то приложению доступ для записи в какую-то папку, а потом передумали и хотите отобрать. Есть ли такая возможность? К счастью да. Но, с потерями. Первый, очень радикальный способ - можно удалить программу и заново ее установить. Второй способ не столь кардинален, но тоже не идеален - надо в Настройках устройства очистить данные приложения. Естественно, вместе с правами доступа, удалятся и другие данные программы, так что, возникает логичный вопрос о практической применимости данного метода. Других методов обнаружить не удалось, возможно, пока. Применять оба способа можно, если предварительно забэкапить данные программы, а после приведения какого-то из методов в действие - восстановить их. Но тут возникает вопрос о возможностях бэкаперов - будут ли бэкапиться только данные, или же данные вместе с разрешениями? Если с разрешениями, то после восстановления, ну, сами понимаете... Все это сильно зависит от конкретных используемых приложений, так что, следует быть очень внимательными. И это еще один хороший повод подумать, прежде чем увидеть древнюю Москву без санкции соответствующих органов выдать права какому-либо приложению на доступ к файловой системе внешней карточки.

Дальше - больше. Что будет, если вы смените карточку, вставленную в слот вашего планшета? Ответ таков: вам придется отдельно давать доступ к каждой карточке, которую вы вставляете в устройство. В принципе, это, наверное, логично. То есть, дали вы файловому менеджеру доступ к корню карточки, потом вынули ее и вставили другую. Файловый менеджер писать на новую карточку не будет, придется давать доступ заново. Зато когда вы вернете первую карточку в планшет, файловый менеджер будет писать на нее без проблем.

Еще один вопрос. Вот дали вы доступ приложению в какую-то папку, не в корневую. Прошло время и вам понадобилось разрешить доступ на запись этой же программе в другую папку на этой же карточке. Можно это сделать? В принципе, это следствие предыдущего случая, поэтому ответ: да, можно. То есть, система для каждого приложения будет помнить список каталогов, к которым вы дали право доступа на запись.

Теперь нюанс, связанный с приложениями, установленными на планшете для каждого пользователя, профиль которого создан на устройстве. Здесь тоже все логично - для каждого пользователя устройства хранятся свои права доступа по приложениям, то есть, каждый - сам кузнец своего счастья, или, если хотите, сам себе злобный Буратино.

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

А, да, вот так выглядит процесс выдачи прав на запись на microSD карточку для ES Проводнка:

Нам нужна внешняя карточка

Попробуем создать что-нибудь прямо в корне карты






Нам нужно отобразить SD карту


Вот она, наша внешняя карта памяти



Ну вот теперь, пожалуй все.

P.S. Так получилось, что с момента, как я написал этот материал, прошло уже довольно много времени. Опубликовать можно было уже много раз, но я все тянул, непонятно почему. Точнее, я знаю, почему - все решал, как лучше представлять серию снимков экрана, описывающую тот или иной процесс: как просто последовательность вставленных изображений, или же в виде слайд шоу. В конечном счете, решение было принято - использовать (по крайней мере, пока) последовательность изображений. Как все это происходило - тема для отдельного поста. Пока же, все остается как есть, жалко только, что времени потрачено много. Утешает лишь, что не напрасно. К тому же, произошло приятное событие. Я писал в этом сообщении, что доступ к внешней карте памяти будет доступен только тогда, когда авторы программ начнут использовать новое API. В качестве примера программы, в котором новое API не используется, я привел Total Commander. Так вот, пока я медлил с публикацией, Total Commander обновился, и теперь он тоже умеет работать с внешней картой памяти в стиле Android Lollipop. Ниже я привожу серию снимков экрана, описывающих процесс создания нового каталога в корне внешней карты памяти с помощью Total Commander:

Выбираем внешнюю карту памяти
и в меню выбираем нужный пункт
Вот теперь, пожалуй, все.

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

Итак, начнем с теории.

Терминология

Гугл нам говорит, что есть следующие понятия:
  1. Внутренняя (internal ) память - это часть встроенной в телефон карты памяти. При ее использовании по умолчанию папка приложения защищена от доступа других приложений (Using the Internal Storage).
  2. Внешняя (external ) память - это общее «внешнее хранилище», т.е. это может быть как часть встроенной памяти, так и удаляемое устройство. Обычно это часть встроенной памяти, как удаляемое устройство я видел в последний раз на андройде 2.2, где встроенная память была около 2Гб, и подключаемая память становилась внешней (Using the External Storage).
  3. Удаляемая (removable ) память - все хранилища, которые могут быть удалены из устройства без «хирургических» вмешательств.

До версии KitKat 4.4 API не предоставляло функционала для получения путей к внешней памяти. Начиная с этой версии (API 19) появилась функция public abstract File getExternalFilesDirs (String type), которая возвращает массив строк с путями к внутренней и внешней памяти. Но как же быть с нашей SD Card, которая вставлена в слот? Путь к ней мы опять не можем получить.

Результаты поиска

Чтобы ответить на поставленный вопрос я обратился к всезнающему гуглу. Но и он мне не дал четкого ответа. Было рассмотрено множество вариантов определения от использования стандартных функций, которые ведут к внешней памяти, но ничего общего с удаляемыми устройствами хранения данных они не имеют, до обработки правил монтирования устройств (Android же на ядре Linux работает). В последних случаях были использованы «зашитые» пути к папке с примонтироваными устройствами (в различных версиях эта директория разная). Не стоит забывать, что от версии к версии правила монтирования меняются.

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

Описание кода

Был создан класс MountDevice , который содержит в себе путь к устройству, тип устройства и некий хэш.
Типов устройств выделено два (внутреннюю память я не стал трогать, так как к ней доступ можно получить через API системы).

Public enum MountDeviceType { EXTERNAL_SD_CARD, REMOVABLE_SD_CARD }
И был создан класс StorageHelper , который и осуществляет поиск доступных карт памяти.

В классе StorageHelper реализовано два способа поиска - через системное окружение (Environment ) и с использованием утилиты Linux mount , а точнее результата ее выполнения.

Способ первый - Environment
При работе с окружением я использую стандартную функцию getExternalStorageDirectory() для получения информации о внешней памяти. Чтобы получить информацию о удаляемой памяти, я использую переменную окружения "SECONDARY_STORAGE ".

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

Функция fillDevicesEnvirement

String path = android.os.Environment.getExternalStorageDirectory() .getAbsolutePath(); if (!path.trim().isEmpty() && android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) { testAndAdd(path, MountDeviceType.EXTERNAL_SD_CARD); } // Получаем ремувабл String rawSecondaryStoragesStr = System.getenv("SECONDARY_STORAGE"); if (rawSecondaryStoragesStr != null && !rawSecondaryStoragesStr.isEmpty()) { // All Secondary SD-CARDs splited into array final String rawSecondaryStorages = rawSecondaryStoragesStr .split(File.pathSeparator); for (String rawSecondaryStorage: rawSecondaryStorages) { testAndAdd(rawSecondaryStorage, MountDeviceType.REMOVABLE_SD_CARD); } }


Вариант решения взят со stackoverflow . Ответ где-то там внизу.
Способ второй - mount
Так как у меня долго не получалось заставить систему мне сказать путь к удаляемой памяти, я решил искать в сторону примонтированных устройств. В системе есть файлы конфигурации, в которых описаны правила монтирования внешних устройств. Все бы хорошо, но на Android версии 4.* к этому файлу простым смертным доступа нет, поэтому рассматривать этот способ не буду.

Вернемся к утилите mount. При запуске без параметров команда возвращает список смонтированных файловых систем. Удаляемые устройства имеют обычно формат файловой системы FAT, то будем выделять строки, в которых есть характеристика "fat ". Внешняя память будет характеризоваться параметром "fuse ".

Примечание: при использовании такого способа не всегда корректно (скорее всего я что-то не учел) определяются типы смотнтированных устройств. Разницу замечал на разных версиях Android. Поэтому этот способ можно использовать как дополнительный.

Функция fillDevicesProcess

try { Runtime runtime = Runtime.getRuntime(); proc = runtime.exec("mount"); try { is = proc.getInputStream(); isr = new InputStreamReader(is); br = new BufferedReader(isr); while ((line = br.readLine()) != null) { if (line.contains("secure")) continue; if (line.contains("asec")) continue; if (line.contains("fat")) {// TF card String columns = line.split(" "); if (columns != null && columns.length > 1) { testAndAdd(columns, MountDeviceType.REMOVABLE_SD_CARD); } } else if (line.contains("fuse")) {// internal(External) // storage String columns = line.split(" "); if (columns != null && columns.length > 1) { // mount = mount.concat(columns + "\n"); testAndAdd(columns, MountDeviceType.EXTERNAL_SD_CARD); } } } } finally { ... } } catch (Exception e) { ... }

Исходный код всего класса расположен еще нигде не расположен. На днях постараюсь разместить на gitHub.

Кто еще какими способами пользуется?

Теги:

  • android
  • разработка под android
  • sd card
Добавить метки

Я купил новый телефон Samsung Galaxy S5, и сразу столкнулся с проблемой в операционной системе Android. А точнее, версии Андроид 4.4 KitKat. В этой версии Андроид введена новая фишка разработчиков Google.

Теперь приложениям запрещен доступ к редактированию файлов на SD карте! Это приводит к тому, что некоторые приложения неработоспособны!

Я расскажу вам, как обойти эту проблему!

Добрый день, уважаемые читатели нашего блога!

На днях я купил себе новый телефон! Samsung Galaxy S5!

НАСЛАЖДЕНИЕ НОВЫМ ТЕЛЕФОНОМ

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

Ранее у меня уже был телефон HTC (ему уже около 4-5 лет) с Андроид системой. Один из первых телефонов с такой системой (на момент покупки). Я его изучил вдоль и поперек, перепрошивал систему несколько раз, выбрал прошивку Андроид, которая по функционалу много превосходила на тот момент официальную.

Для тех, кто не в курсе, перепрошивка — это замена операционной системы телефона. Ну примерно как переустановка Windows на компьютере.

Прошло много времени, вышло много версий Андроид (на момент покупки у меня была Андроид 2.2). Новые версии и телефоны, на которых они устанавливались, много превосходят мой старенький смартфон. Поэтому я был сильно удивлен мощью, быстротой и функциональностью.

Наслаждался я около 4 суток. Затем наткнулся на проблему!

ПРОБЛЕМА! Файловая система запрещает редактирование файла!

После очередного обновления, операционная система телефона сама обновилась до версии Андроид 4.4

Каково же было мое удивление, когда я не смог отредактировать ни один файл на собственном телефоне! Сначала я грешил на глюки, проверил в разных папках, попробовал отредактировать файл с компьютера (подсоединив кабель к телефону). Никаким способом не получалось редактировать файлы!

Посмотрев информацию об операционной системе в Настройки — Об устройстве — Состояние SE для Android я увидел слово Enforcing.

Это кодовое слово Google означает версию Андроид, где на уровне ядра операционной системы ограничены права приложений! ЕМАЕ! Ну я попал!

Я обратился к интернету за решением проблемы

Вот, что я обнаружил:

Х очу заметить, что мой телефон совсем новый, с гарантией от производителя на 5 лет!

Если получитьRoot-доступ (администраторские права в системе Андроид), то гарантия от производителя (Samsung) исчезает. По правде сказать, некоторые хакерские сайты утверждают, что есть специальные методы вернуть заводской счетчик к первоначальному положению. Заводской счетчик при получании Root-доступа обнуляется. Это показывает обслуживающей компании о том, что телефон был взломан, и гарантия на него не распространяется.

Чтобы повлиять на этот счетчик, нужно разобрать телефон и залезть в микросхемы. Поэтому не ведитесь на такие обманы!

Если вы получите Root-доступ, то гарантия у вас исчезнет! Проверено на своем старом телефоне!


В новой версии Андроид 5.0 Google использует другую логику запрета на доступ к внешним SD-картам памяти.

1. Каким образом работает новая логика?

Приложения устанавливаются в систему в скомпилированном виде. То есть в виде, готовом к запуску. Это увеличивает быстродействие, многозадачность и уменьшает расход батареи.

Иными словами, ранее в системе были файлы дистрибутивов (установочные файлы) и система, при запуске приложения, компилировала его, перед запуском. Иными словами, файлы были запакованные, во время запуска приложения, файлы распаковывались и запускались.

Теперь, в Андроид 5.0, все приложения устанавливаются в скомпилированном, разархивированном виде. Это приводит к тому, что приложения расходуют больше места, но быстрее выполняются!

2. К чему это приводит?

  1. Права доступа задаются приложению ПРИ УСТАНОВКЕ!
  2. Пользователь вправе сам устанавливать права конкретному приложению при установке.
  3. Пользователь может поменять права приложению. Например, если вы доверяете какой-то программе, то можете разрешить ей редактирование файлов на внешней карте памяти.

Я ХОЧУ ОБНОВИТЬ АНДРОИД ДО ВЕРСИИ 5.0, чтобы решить проблему!

Если я обновлю ОФИЦИАЛЬНО версию операционной системы, то я СОХРАНЮ ГАРАНТИЮ!

Но мне нужна версия Андроид для России (именно для нее, а не для другой страны), чтобы не мучиться с английскими словами в интерфейсе.

Попытаюсь найти ее.

Официальная программа поддержки Samsung телефонов Kies говорит, что мой телефон обновлен до последней версии По.

Официальных обновлений Андроид (для нашего региона) я не нашел на официальных сайтах.

На форумах я нашел информацию, что Google выпустила версию прошивки для России в январе 2015. Но очень медленно внедряется на аппараты клиентов.

Таким образом, я могу подождать, пока на мой аппарат не поступит сообщение о том, что есть обновление По. После этого я смогу обновить Андроид.

Можно, не дожидаясь приглашения, самому прошить на новую прошивку.

РАБОТАЮЩИЙ Способ редактирования файлов (с помощью компьютера) на телефоне с Android 4.4 версии Enforcing.

После того, как я решил дождаться официального обновления, стал рыться в файловой системе на телефоне. И обнаружил странное:

  1. Есть права на редактирование папок — перемещение, переименование!
  2. Можно удалять файлы, перемещать в другие папки (как копии)
  3. Нет прав на редактирование файлов!

И мне сразу пришел рабочий способ изменения файла с помощью промежуточной копии! Системный анализ в действии...

То есть что мы делаем:

  1. Открываем файл на редактирование
  2. Сохраняем на компьютере (на телефоне не даст сохранить копию!)
  3. удаляем файл из телефона
  4. переписываем копию на место удаленного файла!

Таким образом мы получаем измененный файл (с помощью промежуточной копии)!

Вот такие дела, товарищи!

Будут новости, сразу отпишусь!

А теперь пока, до скорого!

Похожие статьи