Android 7.0 Scoped Directory Access 實踐

Android 7.0 引進(jìn)了許多新特性和 API娃循,其中有一點(diǎn)被很多人都忽略了,或許是沒有注意到,或許是感覺使用起來比較麻煩,在這里我就基于自己使用的體驗和過程簡述一下這個叫做 “作用域目錄訪問(Scoped Directory Access)” 的新 API缀皱。

權(quán)限請求效果

如圖所示,作用域目錄訪問和 Android 6.0 訪問內(nèi)部儲存空間一樣动猬,需要應(yīng)用程序主動向用戶請求讀寫權(quán)限啤斗。不同的是,作用域目錄訪問不再要求應(yīng)用聲明 android.permission.WRITE_EXTERNAL_STORAGE 權(quán)限赁咙,也限制了應(yīng)用程序訪問內(nèi)部儲存空間行為钮莲,只能在請求的作用域內(nèi)進(jìn)行讀寫操作(包括文件、子文件夾)彼水。

這個 API 看似意義不大崔拥,你獲得了訪問內(nèi)部儲存空間權(quán)限也同樣可以向那些特定訪問域中寫入文件,但對于用戶來說凤覆,能更加放心地讓應(yīng)用程序使用他/她的手機(jī)內(nèi)部儲存链瓦;對開發(fā)者/廠商而言,也嚴(yán)格要求自己不要濫用權(quán)限盯桦,未來高版本 Android 對存儲機(jī)制大改的時候也能很快地應(yīng)對(甚至無需做出任何改動)慈俯。

廢話不多說,下面是作用域目錄訪問特性的使用:

權(quán)限聲明

權(quán)限拥峦?不需要聲明吧……哦贴膘,還要考慮到對舊版本 Android 的兼容,還是需要在 AndroidManifest 中聲明內(nèi)部儲存權(quán)限事镣,同時傳統(tǒng)的訪問方式應(yīng)當(dāng)保留下來步鉴,對 Build.VERSION.SDK_INT 進(jìn)行判斷再采取不同的措施。

對于 Android 7.0璃哟,可以嘗試更加激進(jìn)的權(quán)限聲明:

<!-- maxSdkVersion 指定這個權(quán)限只在哪個版本以下使用 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="24"/>

如果你的應(yīng)用程序最低 SDK 版本就是 25氛琢,可以不加上這一行聲明。

申請權(quán)限

使用 StorageManager 類獲取你要訪問的卷的 StorageVolume 實例随闪。
要獲取所有可用卷的列表阳似,包括可移動介質(zhì)卷,請使用 StorageManager.getStorageVolumes()铐伴。

一般撮奏,我們只使用內(nèi)部儲存空間俏讹,只需要 StorageManager.getPrimaryStorageVolume() 獲得主要卷即可。

然后畜吊,通過調(diào)用該實例的 StorageVolume.createAccessIntent() 方法創(chuàng)建一個 intent泽疆,使用此 intent 訪問外部存儲目錄。
createAccessIntent() 需要傳入一個參數(shù)指定你想訪問哪個作用域玲献,只能從以下常量中選擇:

  • 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 在內(nèi)部儲存或者 SD 卡中默認(rèn)生成的公用文件夾。

示例代碼:

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

執(zhí)行了 Intent 后應(yīng)用程序就會彈出請求對話框捌年,等待用戶確認(rèn)瓢娜。

接收請求結(jié)果和目錄 Uri

startActivityForResult() 后待用戶確認(rèn)后,應(yīng)用程序當(dāng)前的 Activity 就會接收到請求結(jié)果礼预,Request code 由自己決定(如前面的 REQUEST_SCOPED_PERMISSION)眠砾。

如果用戶同意了權(quán)限,Result code 為 RESULT_OK托酸,且返回的 Intent 通過 getData() 可以獲得一個目錄 Uri褒颈。

獲得目錄 Uri 后強(qiáng)烈建議馬上調(diào)用:

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

系統(tǒng)將保留此 URI,后續(xù)的訪問請求將返回 RESULT_OK获高,且不會向用戶顯示確認(rèn)對話框哈肖。

訪問文件和文件夾

和傳統(tǒng)的訪問內(nèi)部儲存空間的方式不同,F(xiàn)ile 類不適用于作用域目錄訪問念秧,也不能簡單的拼接相對路徑獲得目標(biāo)文件或子文件夾淤井。

這時候需要 android.support.v4.* 包中的 DocumentFile 類,通過這段代碼:

DocumentFile.fromTreeUri(this, uri)

獲得申請到的作用域目錄 DocumentFile摊趾,使用方式和 File 對象相差不大币狠。

但需要注意的是,要獲取文件或文件夾(包括次級目錄)時砾层,應(yīng)當(dāng)使用 DocumentFile.findFile() 逐步獲得目標(biāo) DocumentFile漩绵。若運(yùn)行過程發(fā)現(xiàn)文件夾不存在,則需要用到 DocumentFile.createDirectory()(請不要當(dāng)作 findDirectory 使用肛炮,它會創(chuàng)建全新的文件夾)止吐。

讀寫文件

得到了目標(biāo)文件 DocumentFile 后,getUri() 即可獲得 Uri 對象侨糟。
然后通過 ContentResolver 打開 FileDescriptor碍扔,便可創(chuàng)建 FileInputStream 文件輸入流或者 FileOutputStream 文件輸出流,進(jìn)行操作了秕重。

// 第二個參數(shù)為讀寫模式不同,"r" 為只讀,"w" 為寫入
ParcelFileDescriptor pfd = context.getContentResolver().openFileDescriptor(uri, "w"); 
FileOutputStream fileOutputStream = new FileOutputStream(pfd.getFileDescriptor());

總結(jié)

基本使用大概就這樣了,如果有什么建議或者錯誤歡迎提出糾正~

文章原文地址: https://feng.moe/archives/11/ 轉(zhuǎn)載前請標(biāo)注原作者和鏈接二拐,謝謝

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末服鹅,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子百新,更是在濱河造成了極大的恐慌企软,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件饭望,死亡現(xiàn)場離奇詭異澜倦,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)杰妓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來碘勉,“玉大人巷挥,你說我怎么就攤上這事⊙槊遥” “怎么了倍宾?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長胜嗓。 經(jīng)常有香客問我高职,道長,這世上最難降的妖魔是什么辞州? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任怔锌,我火速辦了婚禮,結(jié)果婚禮上变过,老公的妹妹穿的比我還像新娘埃元。我一直安慰自己,他們只是感情好媚狰,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布岛杀。 她就那樣靜靜地躺著,像睡著了一般崭孤。 火紅的嫁衣襯著肌膚如雪类嗤。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天辨宠,我揣著相機(jī)與錄音遗锣,去河邊找鬼。 笑死彭羹,一個胖子當(dāng)著我的面吹牛黄伊,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播派殷,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼还最,長吁一口氣:“原來是場噩夢啊……” “哼墓阀!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起拓轻,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤斯撮,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后扶叉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體员串,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年珊皿,在試婚紗的時候發(fā)現(xiàn)自己被綠了截粗。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡达吞,死狀恐怖张弛,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情酪劫,我是刑警寧澤吞鸭,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站覆糟,受9級特大地震影響刻剥,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜滩字,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一造虏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧麦箍,春花似錦酗电、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至话瞧,卻和暖如春嫩与,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背交排。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工划滋, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人埃篓。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓处坪,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子同窘,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評論 2 353

推薦閱讀更多精彩內(nèi)容