为什么 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)
该应用通过 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)