Android 7.0 Scoped Directory Access 实践

Android 7.0 引进了许多新特性和 API,其中有一点被很多人都忽略了,或许是没有注意到,或许是感觉使用起来比较麻烦,在这里我就基于自己使用的体验和过程简述一下这个叫做 “作用域目录访问(Scoped Directory Access)” 的新 API。

权限请求效果

如图所示,作用域目录访问和 Android 6.0 访问内部储存空间一样,需要应用程序主动向用户请求读写权限。不同的是,作用域目录访问不再要求应用声明 android.permission.WRITE_EXTERNAL_STORAGE 权限,也限制了应用程序访问内部储存空间行为,只能在请求的作用域内进行读写操作(包括文件、子文件夹)。

这个 API 看似意义不大,你获得了访问内部储存空间权限也同样可以向那些特定访问域中写入文件,但对于用户来说,能更加放心地让应用程序使用他/她的手机内部储存;对开发者/厂商而言,也严格要求自己不要滥用权限,未来高版本 Android 对存储机制大改的时候也能很快地应对(甚至无需做出任何改动)。

废话不多说,下面是作用域目录访问特性的使用:

权限声明

权限?不需要声明吧……哦,还要考虑到对旧版本 Android 的兼容,还是需要在 AndroidManifest 中声明内部储存权限,同时传统的访问方式应当保留下来,对 Build.VERSION.SDK_INT 进行判断再采取不同的措施。

对于 Android 7.0,可以尝试更加激进的权限声明:

<!-- maxSdkVersion 指定这个权限只在哪个版本以下使用 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="24"/>

如果你的应用程序最低 SDK 版本就是 25,可以不加上这一行声明。

申请权限

使用 StorageManager 类获取你要访问的卷的 StorageVolume 实例。
要获取所有可用卷的列表,包括可移动介质卷,请使用 StorageManager.getStorageVolumes()

一般,我们只使用内部储存空间,只需要 StorageManager.getPrimaryStorageVolume() 获得主要卷即可。

然后,通过调用该实例的 StorageVolume.createAccessIntent() 方法创建一个 intent,使用此 intent 访问外部存储目录。
createAccessIntent() 需要传入一个参数指定你想访问哪个作用域,只能从以下常量中选择:

  • Environment.DIRECTORY_MUSIC
  • Environment.DIRECTORY_PODCASTS
  • Environment.DIRECTORY_RINGTONES
  • Environment.DIRECTORY_ALARMS
  • Environment.DIRECTORY_NOTIFICATIONS
  • Environment.DIRECTORY_PICTURES
  • Environment.DIRECTORY_MOVIES
  • Environment.DIRECTORY_DOWNLOADS
  • Environment.DIRECTORY_DCIM
  • Environment.DIRECTORY_DOCUMENTS

各个常量指定的位置和作用这里就不一一解释了,都是 Android 在内部储存或者 SD 卡中默认生成的公用文件夹。

示例代码:

StorageManager sm = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
Intent intent = sm.getPrimaryStorageVolume().createAccessIntent(Environment.DIRECTORY_PICTURES);
startActivityForResult(intent, REQUEST_SCOPED_PERMISSION);

执行了 Intent 后应用程序就会弹出请求对话框,等待用户确认。

接收请求结果和目录 Uri

startActivityForResult() 后待用户确认后,应用程序当前的 Activity 就会接收到请求结果,Request code 由自己决定(如前面的 REQUEST_SCOPED_PERMISSION)。

如果用户同意了权限,Result code 为 RESULT_OK,且返回的 Intent 通过 getData() 可以获得一个目录 Uri。

获得目录 Uri 后强烈建议马上调用:

getContentResolver().takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

系统将保留此 URI,后续的访问请求将返回 RESULT_OK,且不会向用户显示确认对话框。

访问文件和文件夹

和传统的访问内部储存空间的方式不同,File 类不适用于作用域目录访问,也不能简单的拼接相对路径获得目标文件或子文件夹。

这时候需要 android.support.v4.* 包中的 DocumentFile 类,通过这段代码:

DocumentFile.fromTreeUri(this, uri)

获得申请到的作用域目录 DocumentFile,使用方式和 File 对象相差不大。

但需要注意的是,要获取文件或文件夹(包括次级目录)时,应当使用 DocumentFile.findFile() 逐步获得目标 DocumentFile。若运行过程发现文件夹不存在,则需要用到 DocumentFile.createDirectory()(请不要当作 findDirectory 使用,它会创建全新的文件夹)。

读写文件

得到了目标文件 DocumentFile 后,getUri() 即可获得 Uri 对象。
然后通过 ContentResolver 打开 FileDescriptor,便可创建 FileInputStream 文件输入流或者 FileOutputStream 文件输出流,进行操作了。

// 第二个参数为读写模式,"r" 为只读,"w" 为写入
ParcelFileDescriptor pfd = context.getContentResolver().openFileDescriptor(uri, "w"); 
FileOutputStream fileOutputStream = new FileOutputStream(pfd.getFileDescriptor());

总结

基本使用大概就这样了,如果有什么建议或者错误欢迎提出纠正~

文章原文地址: https://feng.moe/archives/11/ 转载前请标注原作者和链接,谢谢

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容