Для API ›= 29 параметры доступа к хранилищу изменились настолько радикально, что даже наличие пути к файлу не будет иметь большого значения из-за ограничений, требуемых разрешений и т. д.
Документация для API 30 (Andorid 11), в которой говорится, что можно использовать более старый File API, ссылается на тот факт, что такой API теперь можно использовать для доступа/работы с файлами, расположенными в папках общего хранилища, поэтому DCIM, Music и т. д. Вне этих мест файловый API не будет работать ни в Android 10.
Обратите внимание, что File API в Android 11 и всех будущих версиях работает намного медленнее со штрафом, потому что он был переписан в оболочку, поэтому больше не является API файловой системы. Внизу теперь делегирует все операции в MediaStore. Если важна производительность, то лучше использовать непосредственно MediaStore API.
Я бы рекомендовал работать только с Uris / файловыми дескрипторами через ContentResolver и отказаться от реальных путей к файлам. Любой обходной путь будет временным, и его будет сложнее поддерживать с каждой новой версией Android.
Следующий пример (немного хакерский) может помочь разрешить путь к файлу в API 29 и 30; не тестировалось ниже 29. Не является официальным, поэтому нет гарантии, что он будет работать во всех случаях использования, и я не рекомендую использовать его в производственных целях. В моих тестах разрешенный путь отлично работает с MediaScannerConnection, но может потребоваться корректировки для удовлетворения других требований.
@Nullable
public static File getFile(@NonNull final Context context, @NonNull final DocumentFile document)
{
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q)
{
return null;
}
try
{
final List<StorageVolume> volumeList = context
.getSystemService(StorageManager.class)
.getStorageVolumes();
if ((volumeList == null) || volumeList.isEmpty())
{
return null;
}
// There must be a better way to get the document segment
final String documentId = DocumentsContract.getDocumentId(document.getUri());
final String documentSegment = documentId.substring(documentId.lastIndexOf(':') + 1);
for (final StorageVolume volume : volumeList)
{
final String volumePath;
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q)
{
final Class<?> class_StorageVolume = Class.forName("android.os.storage.StorageVolume");
@SuppressWarnings("JavaReflectionMemberAccess")
@SuppressLint("DiscouragedPrivateApi")
final Method method_getPath = class_StorageVolume.getDeclaredMethod("getPath");
volumePath = (String)method_getPath.invoke(volume);
}
else
{
// API 30
volumePath = volume.getDirectory().getPath();
}
final File storageFile = new File(volumePath + File.separator + documentSegment);
// Should improve with other checks, because there is the
// remote possibility that a copy could exist in a different
// volume (SD-card) under a matching path structure and even
// same file name, (maybe a user's backup in the SD-card).
// Checking for the volume Uuid could be an option but
// as per the documentation the Uuid can be empty.
final boolean isTarget = (storageFile.exists())
&& (storageFile.lastModified() == document.lastModified())
&& (storageFile.length() == document.length());
if (isTarget)
{
return storageFile;
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
person
PerracoLabs
schedule
30.12.2020
MediaStore
) - person ianhanniballake   schedule 15.04.2020