这将为您提供所选文件夹的实际路径,它仅适用于属于本地存储的文件/文件夹。
Uri treeUri = data.getData()
String path = FileUtil.getFullPathFromTreeUri(treeUri,this)
其中FileUtil是以下类
public final class FileUtil {
private static final String PRIMARY_VOLUME_NAME = "primary";
@Nullable
public static String getFullPathFromTreeUri(@Nullable final Uri treeUri, Context con) {
if (treeUri == null) return null;
String volumePath = getVolumePath(getVolumeIdFromTreeUri(treeUri),con);
if (volumePath == null) return File.separator;
if (volumePath.endsWith(File.separator))
volumePath = volumePath.substring(0, volumePath.length() - 1);
String documentPath = getDocumentPathFromTreeUri(treeUri);
if (documentPath.endsWith(File.separator))
documentPath = documentPath.substring(0, documentPath.length() - 1);
if (documentPath.length() > 0) {
if (documentPath.startsWith(File.separator))
return volumePath + documentPath;
else
return volumePath + File.separator + documentPath;
}
else return volumePath;
}
private static String getVolumePath(final String volumeId, Context context) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
return null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
return getVolumePathForAndroid11AndAbove(volumeId, context);
else
return getVolumePathBeforeAndroid11(volumeId, context);
}
private static String getVolumePathBeforeAndroid11(final String volumeId, Context context){
try {
StorageManager mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
Class<?> storageVolumeClazz = Class.forName("android.os.storage.StorageVolume");
Method getVolumeList = mStorageManager.getClass().getMethod("getVolumeList");
Method getUuid = storageVolumeClazz.getMethod("getUuid");
Method getPath = storageVolumeClazz.getMethod("getPath");
Method isPrimary = storageVolumeClazz.getMethod("isPrimary");
Object result = getVolumeList.invoke(mStorageManager);
final int length = Array.getLength(result);
for (int i = 0; i < length; i++) {
Object storageVolumeElement = Array.get(result, i);
String uuid = (String) getUuid.invoke(storageVolumeElement);
Boolean primary = (Boolean) isPrimary.invoke(storageVolumeElement);
if (primary && PRIMARY_VOLUME_NAME.equals(volumeId))
return (String) getPath.invoke(storageVolumeElement);
if (uuid != null && uuid.equals(volumeId))
return (String) getPath.invoke(storageVolumeElement);
}
return null;
} catch (Exception ex) {
return null;
}
}
@TargetApi(Build.VERSION_CODES.R)
private static String getVolumePathForAndroid11AndAbove(final String volumeId, Context context) {
try {
StorageManager mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
List<StorageVolume> storageVolumes = mStorageManager.getStorageVolumes();
for (StorageVolume storageVolume : storageVolumes) {
if (storageVolume.isPrimary() && PRIMARY_VOLUME_NAME.equals(volumeId))
return storageVolume.getDirectory().getPath();
String uuid = storageVolume.getUuid();
if (uuid != null && uuid.equals(volumeId))
return storageVolume.getDirectory().getPath();
}
return null;
} catch (Exception ex) {
return null;
}
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private static String getVolumeIdFromTreeUri(final Uri treeUri) {
final String docId = DocumentsContract.getTreeDocumentId(treeUri);
final String[] split = docId.split(":");
if (split.length > 0) return split[0];
else return null;
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private static String getDocumentPathFromTreeUri(final Uri treeUri) {
final String docId = DocumentsContract.getTreeDocumentId(treeUri);
final String[] split = docId.split(":");
if ((split.length >= 2) && (split[1] != null)) return split[1];
else return File.separator;
}
}
更新:
针对评论中提到的下载问题:如果您在默认的Android文件选择器中从左侧抽屉中选择下载
,则实际上并未选择一个目录。 下载是一个提供程序。 正常的文件夹树URI看起来像这样:
content://com.android.externalstorage.documents/tree/primary%3ADCIM
下载页面的树形 URI 是:
content://com.android.providers.downloads.documents/tree/downloads
你可以看到一个说externalstorage
,而另一个说providers
。这就是为什么它无法与文件系统中的目录匹配。因为它不是一个目录。
解决方案:你可以添加一个相等检查,如果树形uri等于那个值,则返回默认的下载文件夹路径,可以像这样检索:
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
如果您愿意,可以对所有提供商执行类似操作。我认为它会在大多数情况下
正常工作。但我想象中也有一些边缘情况它可能无法正常工作。
感谢@DuhVir支持Android R案例。