背景
从 Lollipop 开始,应用程序可以访问真正的 SD 卡(在 Kitkat 上无法访问,并且在以前的版本上尚未得到官方支持之后),正如我在这里询问的那样。
问题
因为看到支持 SD 卡的 Lollipop 设备已经变得非常罕见,而且因为模拟器并没有能力(或者是吗?)模拟 SD 卡支持,所以我花了很长时间来测试它.
无论如何,似乎不是使用普通的 File 类来访问 SD 卡(一旦你获得了它的权限),你需要使用 Uris,使用 DocumentFile 。
这限制了对正常路径的访问,因为我找不到将 Uris 转换为路径的方法,反之亦然(而且这很烦人)。这也意味着我不知道如何检查当前的 SD 卡是否可以访问,所以我不知道何时请求用户读取/写入它(或他们)的权限。
我试过的
目前,这是我获取所有 SD 卡路径的方式:
/**
* returns a list of all available sd cards paths, or null if not found.
*
* @param includePrimaryExternalStorage set to true if you wish to also include the path of the primary external storage
*/
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static List<String> getExternalStoragePaths(final Context context,final boolean includePrimaryExternalStorage)
{
final File primaryExternalStorageDirectory=Environment.getExternalStorageDirectory();
final List<String> result=new ArrayList<>();
final File[] externalCacheDirs=ContextCompat.getExternalCacheDirs(context);
if(externalCacheDirs==null||externalCacheDirs.length==0)
return result;
if(externalCacheDirs.length==1)
{
if(externalCacheDirs[0]==null)
return result;
final String storageState=EnvironmentCompat.getStorageState(externalCacheDirs[0]);
if(!Environment.MEDIA_MOUNTED.equals(storageState))
return result;
if(!includePrimaryExternalStorage&&VERSION.SDK_INT>=VERSION_CODES.HONEYCOMB&&Environment.isExternalStorageEmulated())
return result;
}
if(includePrimaryExternalStorage||externalCacheDirs.length==1)
{
if(primaryExternalStorageDirectory!=null)
result.add(primaryExternalStorageDirectory.getAbsolutePath());
else
result.add(getRootOfInnerSdCardFolder(externalCacheDirs[0]));
}
for(int i=1;i<externalCacheDirs.length;++i)
{
final File file=externalCacheDirs[i];
if(file==null)
continue;
final String storageState=EnvironmentCompat.getStorageState(file);
if(Environment.MEDIA_MOUNTED.equals(storageState))
result.add(getRootOfInnerSdCardFolder(externalCacheDirs[i]));
}
return result;
}
private static String getRootOfInnerSdCardFolder(File file)
{
if(file==null)
return null;
final long totalSpace=file.getTotalSpace();
while(true)
{
final File parentFile=file.getParentFile();
if(parentFile==null||parentFile.getTotalSpace()!=totalSpace)
return file.getAbsolutePath();
file=parentFile;
}
}
这就是我检查我可以到达的 Uris 的方式:
final List<UriPermission> persistedUriPermissions=getContentResolver().getPersistedUriPermissions();
这是访问 SD 卡的方法:
startActivityForResult(new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE),42);
public void onActivityResult(int requestCode,int resultCode,Intent resultData)
{
if(resultCode!=RESULT_OK)
return;
Uri treeUri=resultData.getData();
DocumentFile pickedDir=DocumentFile.fromTreeUri(this,treeUri);
grantUriPermission(getPackageName(),treeUri,Intent.FLAG_GRANT_READ_URI_PERMISSION|Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
getContentResolver().takePersistableUriPermission(treeUri,Intent.FLAG_GRANT_READ_URI_PERMISSION|Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
问题
是否可以检查当前的 SD 卡是否可以访问,哪些不是,并以某种方式要求用户获得对它们的许可?
是否有官方方法可以在 DocumentFile uris 和真实路径之间进行转换?我找到了这个答案,但在我的情况下它崩溃了,而且看起来很糟糕。
是否可以向用户请求有关特定路径的许可?甚至可能只显示“你接受是/否吗?”的对话框?
授予权限后,是否可以使用普通的 File API 而不是 DocumentFile API?
给定一个文件/文件路径,是否可以只请求访问它的权限(并检查它是否之前给出),或者它的根路径?
是否可以使模拟器具有SD卡?目前,它提到了“SD卡”,但它作为主要的外部存储,我想使用辅助外部存储对其进行测试,以尝试使用新的 API。
我认为对于其中一些问题,一个对回答另一个有很大帮助。