Android存儲挖坑記

nqgh0280 8年前發布 | 22K 次閱讀 Android Android開發 移動開發

最近在搞Android存儲相關的業務,什么Internal/External/Primary/Secondary搞得我都看懵了,國內也沒什么好的文章系統的講這個,我就挖挖各類資料,整理一下。

1. Internal vs External

對于Internal Storage 與 External Storage,官方文檔上有這么一段話,描述得很詳細了,我翻譯了一段下來:

所有的Android設備都有兩塊存儲區域:Internal Storage和External Storage。它們的名稱來源于早期的Android系統,那時候大家的手機都內置(Permanent)一塊較小存儲板(即Internal Storage),并配上一個的外置的(Removable)儲存卡(即External Storage)。后來部分手機開始將最初定義的“Internal Storage”,即內置存儲,分成Internal和External兩部分。這樣一來就算沒有外置儲存,手機也有Internal和External兩塊存儲區域。這兩塊存儲區域的區別是:

  Internal Storage External Storage
可信度 永遠可用(Permanent) 可能不可用,最典型的當設備作為USB存儲被mount時不可用
訪問權限 App存儲內容僅App本身(或共享uid的App)可訪問(Root除外) App存儲內容全局可讀
內容持久 App存儲內容隨App卸載而消失 當App卸載時,只有存在getExternalFilesDir()路徑下的文件會消失
適應情況 存儲內容僅App自己訪問時的最佳選擇 存儲內容希望與其他App共享或傳到電腦上,但是不想申請任何權限時的最佳選擇

注:此處討論的訪問權限是應用路徑下的權限。

總結下來,External存儲區域有幾個好處:

  1. 可以傳到電腦上;
  2. 可以與其他app共享;
  3. 在4.4之后的App路徑(Android/data/包名)下讀寫不需任何權限;
  4. 存在App路徑之外的文件不會隨App卸載。

相應的,也有幾個缺點:

  1. 可能不可用;
  2. 會被其他應用讀到;
  3. 在非App路徑下寫、修改文件需要權限。

1.1 External Storage的權限

在Internal Storage的App路徑下(/data/data/包名下),App的讀寫操作無需任何權限,我們只需要總結一下External Storage的情況:

Android版本
4.4以下 無需權限 需要申請WRITE_EXTERNAL_STORAGE
4.4及以上 無需權限 在App目錄之外寫,需要申請WRITE_EXTERNAL_STORAGE

關于讀External的權限,在Android Developer上有這樣一段話:

目前,所有App都可以讀External存儲而不需要任何權限,這一點可能會在未來做出改變。如果你希望讀External存儲,那最好申請一下READ_EXTERNAL_STORAGE權限。另外,寫權限已經默認包含了讀權限了。

正常情況下,你用任何文件管理器,點開的根目錄就是你的External存儲。你可以到它下面的應用目錄,你會發現,就算是各個包名下的文件,你也是看得到的。

1.2 多用戶

在4.2及以上的Android系統中引入了多用戶機制。你可能會發現在存儲路徑后面有’0’/‘1’的字樣(如/storage/emulated/0/),這后面的數字表示用戶。主用戶后面為0。

2. Primary vs Secondary

這個Primary和Secondary是怎么來的呢?實際上最開始Android也沒有考慮這個區分,但是后來有一個情況發生了,就是上面所說到的:

后來部分手機開始將最初定義的“Internal Storage”,即內置存儲,分成Internal和External兩部分。

那么如果這個時候手機再插入sd卡,那不是有多個External Storage了嗎?

這個時候,從Internal Storage里面分出來的那塊“External Storage”我們稱之為主存儲(Primary Storage),插入的外置儲存稱之為副存儲(Secondary Storage)

主存儲路徑的獲取方式非常簡單,可以通過Environment.getExternalStorageDirectory()或者Context.getExternalFilesDir(null)來獲取。

副存儲路徑在4.4及以上的Android系統中,可以使用Context.getExternalFilesDirs(null)(注意最后多了一個’s’),它返回的是一個字符串數組。第0個就是主存儲路徑,第1個是副存儲路徑(如果有的話)。

4.4及以下系統中,的副存儲的獲取方式就是一個大坑了,一個一個介紹一下筆者看到過的方法。

2.1 副儲存路徑-StorageManager

在Android中可以通過context.getSystemService(STORAGE_SERVICE)來獲取到StorageManager,但是很可惜的是,它里面有價值的方法都是hide的。。

慶幸的是還有反射。我們可以調用getVolumeList()函數,這個返回的List里面,主存儲是第0個,副存儲(如果有的話)是第1個。
你可以看到Environment.getExternalStorageDirectory()里面就是用它實現的,可以說這個方法是目前最穩妥的。它通過系統的MountService來獲取已mount上來的設備,并且能夠通過StorageVolume知道該存儲是否removable、是否是emulated、mount狀態等等。

涉及到存儲,由于Android rom千奇百怪,不可能是萬全的。如果反射出來的方法缺少變量、方法,或者有別的什么坑,那只能試一下其他方法來保底。

靠譜程度:99%

2.2 副存儲路徑-讀配置xml

com.android.internal.R.xml.storage_list.xml可以獲取到系統的VolumeList,但是這種方法是行不通的,我們可以從源碼中看看。

在6.0以前的MountService上面看到readStorageList()這個函數,它在構造函數里面就會被調用,就是在讀取這個xml文件。但是我們可以看到它并沒有在Volume改變的時候被動態寫入。

并且參考AOSP Document,這個xml文件里面存儲的就是廠商配置的分區,它根本無法更新removable存儲的熱插拔信息

注意:這個xml在6.0被移除了(參考AOSP Document)

靠譜程度:0%

2.3 副存儲路徑-mount命令

執行Linux shell下的mount命令,遍歷每個mount點,從中找到副存儲。

目前,它確實能夠列出副存儲。但是同時會列出很多很多mount點,包括系統mount點,目前好像沒有已知的靠譜方法能夠從中準確找出副存儲。副存儲的命名是沒有規律的,枚舉排除系統mount點的方法不能夠100%確保準確性。

靠譜程度:10%

2.4 副存儲路徑-讀vold.fstab文件

解析/etc/void.fstab,從中找到副存儲位置。

Vold(Volume Daemon)ServiceManager與kernel層之間的橋梁,它對于Volume的信息維護在/etc/vold.fstab中。

一聽就是一個奇怪的方法,文件位置、信息也可能被各類廠商篡改,還可能存在瞬時不一致的情況,不要考慮它。有興趣的同學可以研究一下android-storage-vold

靠譜程度:0%

總結

總結出Android手機目前的幾種存儲方式:

在6.0之前

6.0之前,所有的存儲類型都是Traditional Storage。它支持多用戶、模擬External存儲。由于是MBR分區,存儲上線為2TB。

  1. Physical Primary 最原始的樣子是只有機身自帶的Internal存儲和以External存在的外置存儲,這時候只有一個主存儲,并且它是Physical的。

  2. Emulated Primary (Optional Physical Secondary) 之前所說,從Internal Storage分出一塊來給External Storage。這塊存儲空間就是在Permanent存儲版中”模擬“上去的。所以你可以看到主存儲經常有emulated字樣。 如果這時候還能再插SD卡,則會多一個Physical的Secondary存儲。

在6.0之后

正常情況下,它的存儲方式與之前的兩種相同,不過多了一種新的存儲方式:Adoptable Storage

Adoptable Storage

由于External Storage的缺點(有時不可用,存儲內容沒有被保護),在6.0之后多出了Adoptable存儲方式。

當Android系統Adopt了一塊External存儲區域的時候,它會被視為Internal Storage,同時會被格式化與加密。格式化之后是GPT分區,存儲上線為9ZB。

當你在一個支持Adoptable Storage的手機上插入一個sd卡,它會提示你是否將這個sd卡格式化并用作Internal Storage,或者正常作為External Storage使用。

推薦一篇文章:

CommonsWare’s post,從不同角度詮釋了Internal&External Storage, 非常不錯!

來自:http://blog.desmondyao.com/2016/05/04/android-storage/

 本文由用戶 nqgh0280 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!