Intereting Posts
Идентификаторы ресурсов Android внезапно не окончательны, switch () не работает CommitAllowingStateLoss () в действиях фрагмента Что произойдет, если служба запущена несколько раз? Как заставить текстовое представление Android выглядеть так, как будто оно отключено? Несколько новых продуктов Proguard вызывают сервисы Google Play v10.2.6 до v11.0.0 в проекте Multidex Android Studio – отладочное хранилище ключей Как автовоспроизвести HTML5 mp4-видео на Android? Слушайте исходящие SMS или отправленные сообщения в Android Как получить количество непрочитанных писем gmail (на android) Android разные фоны EditText для разных состояний, используя фигуры, селектор или список Android: можно прикрепить файл к электронной почте без записи на SD? Какие размеры использовать для 4×2 appwidget Ошибка сборки Android из-за неправильной версии java Предупреждение показывает, когда я использую Hash Map In android (используйте новый SparseArray <String>) Ошибка AdActivity в AdMob (SDK 7.0) для Android

Ошибка при записи файлов с Android Storage Access на Lollipop

Задний план

У меня есть несколько приложений, которые сильно используют SD-карту для синхронизации файлов. Сломанный внешний доступ к SD-карте на Kitkat по-прежнему является большой проблемой, но я пытаюсь разрешить это с помощью нового API, доступного на Lollipop для пользователей, у которых это есть.

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

Узнайте больше о том, как это делается здесь: как использовать-new-sd-card-access-api-present-for-lollipop

Затем пользователь может выбрать любую папку / подпапку для синхронизации, и я сохраняю документ папки Uri в виде строки в базе данных.

проблема

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

Затем я создаю новый экземпляр DocumentFile из сохраненной строки и пытаюсь перечислить файлы:

DocumentFile dir = DocumentFile.fromTreeUri(ctx, Uri.parse(storedUri)); dir.listFiles(); 

Проблема заключается в том, что listFiles всегда возвращает дочерние элементы в корневой папке Uri и никогда не является дочерью фактического Uri, я предоставляю метод DocumentFile.fromTreeUri.

Я изучил исходный код DocumentFile, и кажется, что там есть ошибка, в частности, я не вижу необходимости в дальнейшем модифицировать Uri:

 public static DocumentFile fromTreeUri(Context context, Uri treeUri) { final int version = Build.VERSION.SDK_INT; if (version >= 21) { return new TreeDocumentFile(null, context, DocumentsContractApi21.prepareTreeUri(treeUri)); } else { return null; } 

Если мы посмотрим на источник DocumentsContractApi21.prepareTreeUri, мы увидим, что перестраивает Uri:

  public static Uri prepareTreeUri(Uri treeUri) { return DocumentsContract.buildDocumentUriUsingTree(treeUri, DocumentsContract.getTreeDocumentId(treeUri)); } 

И методы, которые он называет:

  public static Uri buildDocumentUriUsingTree(Uri treeUri, String documentId) { return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) .authority(treeUri.getAuthority()).appendPath(PATH_TREE) .appendPath(getTreeDocumentId(treeUri)).appendPath(PATH_DOCUMENT) .appendPath(documentId).build(); } public static String getTreeDocumentId(Uri documentUri) { final List<String> paths = documentUri.getPathSegments(); if (paths.size() >= 2 && PATH_TREE.equals(paths.get(0))) { return paths.get(1); } throw new IllegalArgumentException("Invalid URI: " + documentUri); } 

Найденный идентификатор документа getTreeDocumentId всегда будет соответствовать корневому идентификатору Uri, независимо от того, с каким вызовом вызывается метод Uri. Это делает невозможным перечислить дочерние папки с помощью предоставленных методов инфраструктуры.

Решение

Исправьте метод fromTreeUri, чтобы не всегда использовать корневой документ Uri id.

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

  Class<?> c = Class.forName("android.support.v4.provider.TreeDocumentFile"); Constructor<?> constructor = c.getDeclaredConstructor(DocumentFile.class, Context.class, Uri.class); constructor.setAccessible(true); DocumenFile dir = (DocumentFile) constructor.newInstance(null, mCtx, treeUri); dir.listFiles(); 

Solutions Collecting From Web of "Ошибка при записи файлов с Android Storage Access на Lollipop"