and*_*per 25 android uri file android-sdcard documentfile
从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;
}
}
Run Code Online (Sandbox Code Playgroud)
这就是我如何检查我可以达到的Uris:
final List<UriPermission> persistedUriPermissions=getContentResolver().getPersistedUriPermissions();
Run Code Online (Sandbox Code Playgroud)
这是如何访问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);
}
Run Code Online (Sandbox Code Playgroud)
是否可以检查当前的SD卡是否可访问,哪些不是,并以某种方式要求用户获得他们的许可?
是否有官方方式在DocumentFile uris和真实路径之间进行转换?我找到了这个答案,但它在我的情况下崩溃了,而且看起来很糟糕.
是否可以请求用户对特定路径的许可?甚至可能只显示"你接受是/否?"的对话框?
授予权限后,是否可以使用普通的File API而不是DocumentFile API?
给定一个文件/文件路径,是否可以只是请求访问它的权限(并检查它是否在之前给出)或其根路径?
是否有可能使仿真器具有SD卡?目前,它提到了"SD卡",但它作为主要的外部存储器,我想使用辅助外部存储器进行测试,以便尝试使用新的API.
我认为对于其中一些问题,一个人可以帮助很多人回答另一个问题.
use*_*723 12
是否可以检查当前的SD卡是否可访问,哪些不是,并以某种方式要求用户获得他们的许可?
这有三个部分:检测,有什么卡,检查卡是否已安装,以及要求访问.
如果设备制造商是好人,您可以通过使用参数调用getExternalFiles来获取"外部"存储列表null(请参阅关联的javadoc).
他们可能不是好人.并非每个人都有最新,最热,无错误的操作系统版本,并定期更新.因此可能会有一些目录未列出(例如OTG USB存储等).如有疑问,您可以从文件中获取完整的OS挂载列表/proc/self/mounts.这个文件是Linux内核的一部分,它的格式在这里记录.您可以使用/proc/self/mounts作为备份的内容:解析它,找到可用的文件系统(fat,ext3,ext4等)并删除输出的重复项getExternalFilesDirs.以某些非突兀的名称向用户提供剩余选项,例如"misc目录".这将为您提供所有可能的外部,内部和任何存储空间.
编辑:在给出上述建议后,我终于尝试自己跟进.它到目前为止工作正常,但要注意,不是/proc/self/mounts你最好解析/pros/self/mountinfo(前者仍然可以在现代Linux中使用,但后来是更好,更强大的替代品).在根据安装列表的内容进行假设时,还要确保考虑原子性问题.
如果目录是可读/可写的,你可以通过调用canRead和canWrite来天真地检查它.如果这成功了,那就没有必要做额外的工作了.如果没有,您要么拥有持久的Uri权限,要么不拥有.
"获得许可"是一个丑陋的部分.AFAIK,在SAF基础设施中无法做到这一点.Intent.ACTION_PICK听起来像是可以工作的东西(因为它接受了一个Uri,从中可以选择),但事实并非如此.也许,这可以被认为是一个错误,应该向Android bug跟踪器报告.
给定一个文件/文件路径,是否可以只是请求访问它的权限(并检查它是否在之前给出)或其根路径?
这ACTION_PICK是为了什么.同样,SAF选择器不支持ACTION_PICK开箱即用.第三方文件管理员可能会,但实际上很少有人会授予您真正的访问权限.如果您愿意,也可以将此报告为错误.
编辑:这个答案是在Android Nougat问世之前写的.从API 24开始,仍然无法请求对特定目录进行细粒度访问,但至少可以动态请求访问整个卷:确定包含文件的卷,并使用带参数的getAccessIntent请求访问null(对于辅助卷)或通过请求WRITE_EXTERNAL_STORAGE权限(对于主卷).
是否有官方方式在DocumentFile uris和真实路径之间进行转换?我找到了这个答案,但它在我的情况下崩溃了,而且看起来很糟糕.
没有永不.您永远不会屈服于存储访问框架创建者的想法!邪恶的笑声
实际上,有一种更简单的方法:只需打开Uri并检查创建的描述符的文件系统位置(为简单起见,仅限Lollipop版本):
public String getFilesystemPath(Context context, Uri uri) {
ContentResolver res = context.getContentResolver();
String resolved;
try (ParcelFileDescriptor fd = res.openFileDescriptor(someSafUri, "r")) {
final File procfsFdFile = new File("/proc/self/fd/" + fd.getFd());
resolved = Os.readlink(procfsFdFile.getAbsolutePath());
if (TextUtils.isEmpty(resolved)
|| resolved.charAt(0) != '/'
|| resolved.startsWith("/proc/")
|| resolved.startsWith("/fd/"))
return null;
} catch (Exception errnoe) {
return null;
}
}
Run Code Online (Sandbox Code Playgroud)
如果上述方法返回一个位置,您仍然需要访问以使用它File.如果它没有返回位置,则有问题的Uri不会引用文件(即使是临时文件).它可能是网络流,Unix管道,等等.您可以从此答案中获取上述适用于较旧Android版本的方法版本.它适用于任何 Uri,任何ContentProvider - 不仅仅是SAF - 只要Uri可以打开openFileDescriptor(例如它来自Intent.CATEGORY_OPENABLE).
请注意,如果您考虑官方Linux API的任何部分,则上述方法可视为官方方法.很多Linux软件使用它,我也看到它被一些AOSP代码使用(例如在Launcher3测试中).
编辑:Android Nougat引入了多项安全更改,最明显的是更改了应用程序专用目录的权限.这意味着,自从API 24以来,当Uri引用应用程序专用目录中的文件时,上面的代码段将始终失败并显示异常.这是按照预期的:你不再希望知道这条路.即使您通过其他方式以某种方式确定文件系统路径,也无法使用该路径访问文件.即使其他应用程序与您合作并将文件的权限更改为世界可读,您仍然无法访问它.这是因为如果您没有对路径中某个目录的搜索访问权限,则Linux不允许访问文件.因此,从ContentProvider接收文件描述符是访问它们的唯一方法.
授予权限后,是否可以使用普通的File API而不是DocumentFile API?
你不能.至少不在牛轧糖上.普通文件API进入Linux内核以获取权限.根据Linux内核,您的外部SD卡具有限制性权限,阻止您的应用程序使用它.Storage Access Framework提供的权限由SAF管理(IIRC存储在某些xml文件中),内核对它们一无所知.您必须使用中间方(存储访问框架)才能访问外部存储.请注意,Linux内核拥有自己的机制来管理对目录子树的访问(称为bind-mounts),但Storage Access Framework创建者要么不知道它,要么不想使用它.
File通过使用从Uri创建的文件描述符,您可以获得一定程度的访问权限(几乎可以做任何事情).我建议你阅读这个答案,它可能有一些关于使用文件desriptors一般和Android相关的有用信息.
| 归档时间: |
|
| 查看次数: |
5898 次 |
| 最近记录: |