背景
从Lollipop开始,应用可以访问真实的SD卡(在Kitkat上无法访问,在之前的版本上可以工作但没有官方支持),就像我在这里提问的一样:here。
问题
由于很少见到支持SD卡的Lollipop设备,而且仿真器并没有真正具有仿真SD卡支持的能力(或者有吗?),因此测试它花费了我相当长的时间。
无论如何,似乎现在需要使用Uris来访问SD卡(一旦获得了许可),而不是使用常规文件类。使用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;
}
}
这是我检查可以访问的URI的方法:
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 uri和真实路径之间进行转换?我找到了这个答案,但在我的情况下它会崩溃,并且看起来像是hack-y。
是否可以请求用户关于特定路径的权限?甚至只显示“您接受是/否?”的对话框吗?
一旦授予权限,是否可以使用普通的File API而不是DocumentFile API?
给定一个文件/路径,是否可能只请求访问它(并检查是否已经授予权限)或其根路径?
是否有可能使模拟器具有SD卡?目前,“SD卡”被提及,但它作为主要外部存储器工作,我想尝试使用次要的外部存储器,以尝试使用新的API。
我认为对于其中一些问题,回答一个问题会很有助于回答其他问题。