Ошибка при выводе списка файлов с платформой Android Storage Access на Lollipop

Фон

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

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

Подробнее о том, как это сделать, читайте здесь: как использовать новую SD-карту-доступ-API-представленный-для-леденца на палочке

Затем пользователь может выбрать любую папку / подпапку для синхронизации, и я сохраняю папку документа 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.

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

  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();

0 ответов

Основная ошибка, похоже, исправлена. Я также столкнулся с этой проблемой, и без каких-либо изменений кода теперь он работает с версией 1.0.1documentfile пакет.

dependencies {
    ...
    implementation 'androidx.documentfile:documentfile:1.0.1'
    ...
}
Другие вопросы по тегам