Ошибка при выводе списка файлов с платформой 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'
...
}