首先咱們先來明確一下什麼是外部存儲區?瀏覽器
外部存儲區的英文名字叫 shared storage,也能更直觀的反映出外部存儲區的概念,它是一塊共享的區域,應用寫到這個區域的內容,取得 shared storage 權限的其餘應用均可以直接訪問。 緩存
咱們看到的這些方法,他們所獲取內容的位置一般都是在這塊外部存儲區。除了前三個分別用於獲取文件、緩存文件、媒體文件目錄的三個方法之外,一般遊戲開發者還需去訪問**obb **文件目錄,能夠經過 getObbDirs 的方法來獲取,這個目錄也是在外部存儲區的。安全
咱們來回顧一下,在 Android ap 上應用存儲區,它大概是一個什麼樣的狀況。網絡
一方面是應用程序,它都有一塊屬於本身的專用的內部存儲區域,也叫應用私有存儲區,全部存儲在這個區域裏面的內容,其餘的應用不管有什麼樣的權限,都沒法去直接訪問。 所以這個區域就特別適合被用來保存那些屬於應用、用戶不須要直接訪問的私有數據。app
另外一方面咱們還有一塊共享的外部存儲區,也就是 shared storage。存儲在這個區域裏的內容,其餘的應用能夠經過申請權限去訪問,用戶也能夠去直接訪問。訪問的方法就是將手機經過 USB 數據線鏈接到電腦,用戶就能夠在電腦端直接去看到這一塊存儲的內容。框架
要查看外部存儲區,也就是 /sdcard 的目錄下的內容應用程序,須要先跟用戶申請權限。通常的狀況下寫到外部存儲區的文件,不會被算入應用所佔空間大小。可是有一個例外,就是咱們能夠看到,在外部存儲區裏常常會有一些以應用的 package 命名的目錄,它是不須要任何的訪問權限的。這些目錄就包括剛纔列舉的那些方法所返回的目錄。因爲這些以應用的 package 命名的目錄,它們是具備很是明確的全部權的結構,所以它所歸屬的應用不須要申請任何權限就能夠直接去訪問。這個全部權的設置,也就確保了當用戶在卸載這款應用的時候,目錄裏面的文件會被系統清除。 編輯器
而咱們都知道在 Android 系統上,存儲是應用程序常用的權限之一。 在Android AP 及 P 之前的版本,咱們看到應用在對於外部存儲區的使用上常常會有這樣一些問題:它致使的直接結果就是用戶常常會發現本身的手機的內存莫名其妙被佔滿了。ide
在 Android Q 上咱們但願經過在明確歸屬、保護應用程序、保護用戶程序這三個方面做出努力,對外部存儲區的使用作出優化,從而能提高用戶的總體使用體驗。post
首先是更明確的空間歸屬,也就是對目錄有一個更明確的全部權結構。咱們經過避免在應用程序被卸載以後,還殘留在用戶磁盤上揮之不去的文件,來解決內存空間使用混亂的問題。 所以在 Android Q 上首先要確保系統可以清楚的知道哪些文件是屬於哪些應用程序的,這也使得用戶能夠更容易地管理他們的文件。固然這也意味着當應用程序被用戶卸載的時候,它寫到磁盤裏邊的內容就會被清除。有些應用被用戶卸載以後,卸載前寫入的一部分數據,用戶是但願在應用卸載以後還能繼續去訪問的,這一塊的內容咱們會在後面有討論。優化
第二個方面是增強應用的私有數據的保護。當應用將文件寫到外部存儲區的時候,有一些狀況不但願其餘的應用能夠去隨意訪問。做爲應用開發者,一般是不但願其餘應用能隨意地去查看這個應用在作什麼。在 Android Q 上,咱們經過禁止其餘的應用能夠隨意地查看這部分數據,來保護應用的特有的私有數據。
第三個方面是增強對用戶數據的保護。當用戶去下載一些私人聊天中的圖片,或者是像銀行類 APP 的對帳對帳單 pdf 文件的時候,用戶固然不但願手機上裝的其餘應用都能去查看這部份內容,從而去收集他的我的隱私信息。在 Android Q 上,咱們確保用戶能對應用訪問數據訪問數據和文件的狀況有所掌控,能知道應用程序在什麼時間訪問了哪些文件內容。
堅持這三點原則,也就意味着咱們正在對 Android 平臺的存儲方式進行一個很是深入的改變。咱們知道應用可能須要一些稍長的時間來適應這些變化,但咱們也認爲從長遠來看,它對 Android 平臺會更有益處,可以讓 Android 成爲一個更好的用戶平臺,也能爲用戶提供更好的隱私保護。
接下來,咱們看一下在 Android Q 上存儲空間的訪問是什麼樣的?
咱們認爲這種方式來獲取文件會更加安全,由於用戶會清楚地知道目前是哪一個應用正在訪問哪一個或哪些文件。此外在 Android Q 上,私有存儲區的工做方式是沒有變化的。
Mediastore 是一個屬於用戶的公用媒體文件集合,它從 Android 發佈的第一個版本就已經存在。
以前咱們就提到 Android Q 上的 MediaStore 會有重大改變,下面咱們來看一下 Android Q 上的 Mediastore 是如何工做的。
咱們舉一些例子來講:好比說你的應用裏面會有一些縮略圖文件,或者是若是你的應用是一個音樂播放類軟件,他極可能會有一些專輯的封面圖片這樣的文件,或者是你的應用是一個美圖類的應用,可能會有一些貼紙這樣的資源文件。那麼這樣這些類型的文件就不適合被放到 Mediastore 裏面。由於一般來講,用戶是不會須要在其餘的應用裏去訪問這些資源文件的,也一樣是不但願應用被卸載以後,這些資源文件還殘留在用戶的內存區域中。
在一些狀況下,開發者可能沒法肯定某些內容是否應該被放入用戶的磁盤空間裏,以及放到哪一個具體的位置。
Android Q上新增了一個 Download 集合,用於存儲全部不適合被放到的 Mediastore(視頻、音頻、圖片)集合裏面的文件。
Mediastore 在提供內容和訪問內容的時候,有哪些使用細節?
如今來看這個代碼示例:如何向 Mediastore 添加內容。
咱們看到高亮的 IS_PENDING 這一行,這裏是把 IS_PENDING 設爲 True 的,表示對應的 item 還沒準備好,但願系統可以暫時隱藏,直到準備好。這個是在執行一些運行時間比較長的處理,或者是等待加載的時候會比較有用。只有 item 的全部者程序,能夠在項目被標記爲 IS_pending 的時候去訪問裏面的內容。一旦你設置好了這些映射以後,而且肯定了 item 的IS_PENDING 狀態,接下來就只須要選擇一個集合,而後講而後將 item 存到裏面就能夠了。
當你的 item 的狀態已經準備好以後,你就能夠將 IS_PENDING 標記設置爲false,來告訴系統說他們如今已經準備好了,能夠供其餘的應用來使用了。
Mediastore 會根據文件的 RELATIVE_PATH 去自動進行分類存儲,可是你也能夠去設置 MediaColumns 裏面的 RELATIVE_PATH,去變動他們在磁盤上的儲存位置。系統會盡可能確保你使用的 RELATIVE_PATH 是一個合理的路徑。好比說對於 image.jpeg 類型的文件,它就會被放置在DCIM或者 photos 目錄。
若是你還想去指定保存到某個特定的存儲設備上,你能夠經過使用 Mediastore 的get ExternalVolumeNames 或者是 get AllVolumeNames,而後去選擇你想要的系統設備上全部的 VolumeNames,把它傳遞給 getContentUri 的方法。另外還有一個接口是 StorageManager 的 Storage Volume 這個方法。它能夠告訴你目前系統連上的全部存儲設備的底層信息。
這一頁的代碼演示的就是如何經過設置 RELATIVE_PATH,將一個音頻文件存儲到指定的設備及其指定的目錄下。
關於 Mediastore 的查詢也有一些須要特別注意的地方,因爲如今是由系統去決定保存的位置,因此 DATA columns 已經被廢棄。 但願你們使用 ContentResolver.query() 和 ContentResolver.openFileDescriptor() 這兩個方法去取代它。咱們在 Q 上有一些兼容性的邏輯來幫助應用適應這種變化。可是這種方案並非特別的完美,因此咱們仍是強烈的建議開發者儘可能去使用咱們在 Q 上提供的 ContentResolver 裏面的接口去實現這個變化。
若是你正在查詢 Mediastore 而後你發現裏面有一些隱藏的,也就是它的 IS_PENDING 標記被設置爲 true 的這些項目,而後你又確認你的應用,真的去須要訪問這些項目。 咱們如今還有一個接口叫作 setincludePending(),經過調用這個接口,你能夠設置獲取到這些被標記爲 IS_PENDING 的 item,可是即便你能獲取到這些 item,你仍是不能去訪問裏面的具體的數據的,只有這些 item owner 的 app 能夠把 IS_PENDING 的標記設置爲 false 以後,才能夠去訪問項目裏面的具體內容。若是應用有須要在媒體代碼裏面去訪問這些打開的文件,首先你須要在你的應用的 Java/Kotlin 代碼當中打開文件,而後把這個打開的 find script 傳遞給你的 code。
咱們這一頁是一個代碼片斷,舉例說明了如何經過 content 和 resolver 接口的方法來對 Mediastore 進行查詢。
Storage 運行時的權限可讓應用去發現並讀取其餘的應用,存入到 Mediastore 裏面的媒體文件,可是若是你須要修改這些文件的話,你須要去獲取 Right_exstorage 權限。若是你的應用沒有獲取到必要的權限,就直接調用了update/delete/openFileDescriptor 這些方法,那就會收到系統拋出的異常。
你能夠經過 try catch 來捕獲這些異常,而且在裏面作一些處理,並且咱們很是鼓勵開發者這樣作。
咱們還注意到在照片文件中,一般還會有一個原始的地理位置的信息,這個信息是便於用戶在以後查看這些照片的時候,還能回憶起這個照片當時是在什麼地方拍攝的。可是地理位置信息對用戶來講一般是比較敏感的我的信息,默認狀況下 Android 會隱藏這個信息。若是你的應用須要去訪問照片的地理位置信息,它須要在應用裏面去聲明 ACCESS_MEDIA_LOCATION 權限,而後還須要去調用 Mediastore 的 Save/Require/Origin 接口來指明須要獲取某個 url 對應的文件的原始信息。
另外在 Mediatore 裏面的 LATITUDE 和 LONGITUDE 這兩個常量字段已經被廢棄了,若是你須要獲取經緯度信息的話,你如今須要使用 ExifInterface 接口來代替。
咱們這裏展現的代碼片斷,演示如何添加 ACCESS_MEDIA_LOCATION 權限,以及如何經過 ExifInterface 接口來獲取文件的經緯度信息。
咱們如今來看一下,在 Android Q 上另外一個引入的新東西叫分區存儲,也叫隔離存儲沙箱。
隔離存儲沙箱可讓系統更好地對應用使用的存儲空間有一個準確的統計,而且能夠去保護應用的程序數據,以及用戶的數據安全。
在應用 target Q 時,它就會自動進入分區存儲模式,也就是沙箱模式了。這也就意味着應用再也不能夠直接訪問SD卡的目錄。若是你在 target Q 的狀況下還去嘗試直接訪問SD卡,這個目錄就會收到報錯。雖然不能夠直接去訪問 SD 卡的根目錄,可是仍是能夠直去訪問以你的應用的 package 命名的目錄。 若是想訪問在你的 package name 目錄之外的其餘的地方的內容,你就必須使用 Mediastore 或者是存儲訪問框架 SAF。
存儲訪問框架容許應用去調出 Android 裏面的系統選擇器,這個系統選擇器會不斷升級,不用擔憂說之後的版本升級 UI 會發生變動。選擇器不只可讓用戶去選擇本地的存儲存儲位置裏面的文件,經過它用戶也能夠去選擇那些存儲在雲端裏面的文件,或者是其餘的應用,經過 content provider 來的提供的那些文件。
咱們剛纔提到過說應用程序它在 Android Q 默認狀況下,會被放到沙箱裏面。而後咱們認爲這樣的設置的變動是適用於絕大多數的用戶場景,好比說用戶若是想選擇一些照片上傳到社交網絡,或者是在文件編輯器中打開和保存文檔,或者是在瀏覽器裏下載文件,這些用戶場景均可以被很好地處理。然而咱們在發佈了 Beta 1 和 Beta 2 這兩個版本以後,收到大量的開發者的反饋,廣泛是說他們須要更長的一段時間來適配這種變化。因此在今年5月初的 IO 上,咱們也宣佈了一個在 Beta 1 和 Beta 2 裏面沒有的一個新東西。 如今應用能夠經過加上一個新的標籤來選擇退出隔離模式。若是你的應用是 Android P 以及如下的版本時,那麼這個標籤就默認是 false。固然你也能夠手動的將它設置爲 true。當你將它設置爲 true 的時候,不管應用的它給它SDK target version 是多少,只要應用運行在 Android Q 系統上,就會啓用隔離存儲沙箱模式。咱們新增的標籤只是一個暫時的解決方案,它的目的是給開發者可以有相對來講更長的一段時間去適配這個變化,而不是說啓用了這個標籤,你就能夠永遠的不受隔離存儲沙箱的控制。
咱們在明年發佈的 Android 下一個大版本中會默認強制開啓限制。除了下載集合之外,Mediastore 裏面的其餘媒體集合是不受隔離存儲沙箱模式影響的。若是你的應用處於隔離存儲沙箱模式,只能訪問它本身向 Mediastore 的 download 集合提供的內容。
咱們這一頁展現了新增的 requestLegacyExternalStorage 這個標籤,經過將這個標籤設置爲 true,你能夠暫時不受沙箱的控制。固然你也能夠經過下面的 isExternalStorageLegacy 這個接口去判斷當前你的應用是否受到沙箱的控制。