为什么 DocumentsContract.getTreeDocumentId(uri) 给我 Uri 参数的父项的文档 ID?

Why is DocumentsContract.getTreeDocumentId(uri) giving me the doc ID for a parent of the Uri argument?

该应用通过 ACTION_OPEN_DOCUMENT_TREE:

将用户发送到 SAF 选择器
void openStoragePicker() {
    String messageTitle = "Choose directory app to use";
    Intent intent = new Intent(ACTION_OPEN_DOCUMENT_TREE);
    startActivityForResult(Intent.createChooser(intent, messageTitle), Dry.REQUEST_CHOOSE_APP_DIR);
}

onActivityResult 中,我们获取持久化权限并存储 Uri 的字符串:

@Override
public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
    switch (resultCode) {
        case Activity.RESULT_OK:
            if (requestCode == Dry.REQUEST_CHOOSE_APP_DIR) {
                if (resultData == null) {
                    Log.d(Dry.TAG, "result data null");
                } else {
                    if (resultData.getData() != null) {
                        Uri uri = resultData.getData();
                        Storage.releasePersistedPermissions(this);
                        getContentResolver().takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
                        Storage.setSharedPrefString(uri.toString(), Storage.SHARED_PREF_APP_DIR_URI, this);
                        dbw.clearAlreadyPlayed();
                    }
                }
            }
            break;
        case Activity.RESULT_CANCELED:
            //
            break;
    }
}

我们在需要时重新创建树 Uri:

static Uri getTheDir(Context context) {
    String result = Storage.getSharedPrefString(SHARED_PREF_APP_DIR_URI, context);
    if (result == DEFAULT_SHARED_PREF_STRING) {
        return null;
    }
    Uri dirUriParsed = Uri.parse(Uri.decode(result));
    Log.d(Dry.TAG, "the dir uri parsed: " + dirUriParsed.toPath());
    return dirUriParsed;
}

我们想要列出文件,我们可以使用显示的模式

static ArrayList<String> getFiles(Context context) {
    ArrayList<String> fileStrings = new ArrayList<>();
    Uri rootUri = getTheDir(context);
    if (rootUri == null) {
        return fileStrings;
    }

    long startTime = System.currentTimeMillis();

    ContentResolver contentResolver = context.getContentResolver();
    String theDocToReturnChildrenFor = DocumentsContract.getTreeDocumentId(rootUri);
    Log.d(Dry.TAG, "theDocToReturnChildrenFor: " + theDocToReturnChildrenFor);
    Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(rootUri, theDocToReturnChildrenFor);
    List<Uri> dirNodes = new LinkedList<>();
    dirNodes.add(childrenUri);
    while(!dirNodes.isEmpty()) {
        childrenUri = dirNodes.remove(0);
        Cursor c = contentResolver.query(childrenUri, new String[]{DocumentsContract.Document.COLUMN_DOCUMENT_ID, DocumentsContract.Document.COLUMN_DISPLAY_NAME, DocumentsContract.Document.COLUMN_MIME_TYPE}, null, null, null);
        try {
            while (c.moveToNext()) {
                final String docId = c.getString(0);
                final String name = c.getString(1);
                final String mime = c.getString(2);
                if (isDirectory(mime)) {
                    if (Arrays.asList(SUBDIRECTORIES_TO_OMIT).contains(name)) {
                        continue;
                    }
                    final Uri newNode = DocumentsContract.buildChildDocumentsUriUsingTree(rootUri, docId);
                    dirNodes.add(newNode);
                } else {
                    for (String ext: SUPPORTED_FILE_EXTENSIONS) {
                        if (name.endsWith(ext)) {
                            fileStrings.add(docId);
                            break;
                        }
                    }
                }
            }
        } finally {
            closeQuietly(c);
        }
    }

    Log.d(Dry.TAG, "fileStrings length: " + fileStrings.size() + "time spent building song list: " + ((System.currentTimeMillis() - startTime) / 1000.0) + "s");
    return fileStrings;
}

但是,只有当目录恰好是存储卷中的顶级目录时,这才会按预期工作。如果用户选择的目录不是卷根的直接子目录,那么,当我们尝试 DocumentsContract.getTreeDocumentId(rootUri) 时,它 returns 不是 的文档 ID URI,而是卷根之前其最高父级的文档 ID!

打印重构 Uri 的日志调用给出了这个输出:

the dir uri parsed: /tree/primary:a test dir/a child test dir/3rd level dir

但是另一个打印文档 ID 的日志调用打印了这个:

theDocToReturnChildrenFor: primary:a test dir

我做的好吗?这是 Android 错误吗?我注意到 描述了此方法的完全相同的行为。该问题可以通过遵循已建立的递归列表模式来解决,但是,该用户说:

It is almost like getTreeDocumentId(rootUri) is returning what getRootId(rootUri) should be returning.

此方法的文档没有帮助,它们很简短并且有错别字,意思不清楚。 DocumentsContract.getTreeDocumentId docs.

应用的目标SDK是30。设备Android版本也是api30(Android11)。

如果有人可以帮助我为用户选择的目录获取正确的文档 ID,我将不胜感激。

Uri dirUriParsed = Uri.parse(Uri.decode(result))

尝试:

  Uri dirUriParsed = Uri.parse(result)