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存儲區域有幾個好處:
- 可以傳到電腦上;
- 可以與其他app共享;
- 在4.4之后的App路徑(
Android/data/包名
)下讀寫不需任何權限; - 存在App路徑之外的文件不會隨App卸載。
相應的,也有幾個缺點:
- 可能不可用;
- 會被其他應用讀到;
- 在非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。
-
Physical Primary 最原始的樣子是只有機身自帶的Internal存儲和以External存在的外置存儲,這時候只有一個主存儲,并且它是Physical的。
-
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/