碼個蛋(codeegg)第 698 次推文
作者: SpannerBear
原文: https://juejin.im/post/5d2c36f5e51d4556db694aa3
Android/ target=_blank class=infotextkey>安卓碎片化的問題,由來已久,這次來看一下文件儲存碎片化的問題。到底要怎么去正確選擇和管理文件存儲呢?
為什么要管理文件?
Android手機一直以來被人詬病越用越卡,越用存儲空間越少,經常有要靠各種清理App清理垃圾,到最后不得對手機進行雙清,原因除了硬件老化和Android的底層實現問題之外,開發者對文件管理的忽視制造出大量無法清理的“垃圾”也是造成手機卡慢的原因之一。
Android的開放性給了開發者巨大的自由度,但自由不是讓我們濫用權限和隨意開發的借口,每一個開發者都應該注重細節,連曾經一片混亂的第三方推送都開始統一整合規范化了,如果你還在隨意開發,不如現在開始,注重細節,提高用戶的Android手機體驗?
Android閃存
總所周知,Android手機存儲分為兩個部分:內部存儲和外部存儲,內部存儲一般是手機自帶的存儲空間,外部存儲指外插SD卡提供的存儲空間;隨著手機發展,這兩個存儲的定義又有了一些些變化,新的手機不再有外插SD卡的概念,采取了內置閃存(eMMC、UFS等)的方式,所以內部存儲和外部存儲在新的Android手機上已經在同一個硬件上了。但為了兼容舊設備和讓用戶得到更好的體驗,我們仍然需要管理好手機上內外存儲的使用。
關于文件存儲位置的api
做過文件相關管理的同學應該都曾經被android眾多的文件api搞得一片混亂過,現在來理一理.我把應用操作的文件存儲位置分為三個部分:
-
應用內部存儲私有文件目錄
-
應用外部存儲私有文件目錄
-
公有目錄
我們有兩種api去獲取這三個部分的存儲位置,它們分別歸屬于Context和Environment。
Context
Context是應用的上下文,它用來獲取與應用相關的文件目錄,可以獲取應用私有和應用公有目錄,常用的api有(后面是所對應的路徑):
1. Context#getCacheDir /data/user/0/cn.appname.xxx/cache
2. Context#getDir("spanner",MODE_PRIVATE) /data/user/0/cn.appname.xxx/app_spanner
3. Context#getFileDir /data/user/0/cn.appname.xxx/files
3. Context#getExternalCacheDir /storage/emulated/0/Android/data/cn.appname.xxx/cache
4. Context#getExternalFilesDir(Environment.DIRECTORY_PICTURES) /storage/emulated/0/Android/data/cn.appname.xxx/files/Pictures
Context#getExternalFilesDir /storage/emulated/0/Android/data/cn.appname.xxx/files
5. Context#getExternalMediaDirs /storage/emulated/0/Android/media/cn.appname.xxx
前兩個是應用內部存儲私有目錄,后面4個都是應用外部存儲私有文件目錄。注意:/data/user/0/ 等同于 /data/data/
Environment
Environment和應用無關,它用于獲取公有存儲位置的文件目錄,常用的api有:
1. Environment#getExternalStorageDirectory /storage/emulated/0
2. Environment#getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) /storage/emulated/0/DCIM
Environment#getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) /storage/emulated/0/Pictures
3. Environment#getDataDirectory /data
4. Environment#getDownloadCacheDirectory /data/cache
5. Environment#getRootDirectory /system
API的選用
到底什么時候要用什么api呢?
應用私有文件目錄
應用私有目錄由Context獲取控制,分為內部存儲和外部存儲,內部存儲不需要申請文件讀寫權限也能夠使用,外部存儲需要權限(getetExternalCacheDir 和 getExternalFilesDir 這兩個方法從4.4之后不再需要讀寫權限)。用戶對app進行數據清理或卸載可以清理外部存儲和內部存儲下的所有文件目錄。
內部存儲
內部存儲的文件夾其他應用和用戶無法直接訪問,可以用于存放敏感數據。
-
getCacheDir
-
專門用于存放緩存數據。
-
用戶對app進行緩存清理的時候會清理緩存目錄cache的數據,手機空間不足的時候系統也會對緩存目錄內的數據進行清理。但盡管如此,開發者仍要管理好緩存數據特別是內部存儲的緩存,避免緩存數據過大。
-
-
getFileDir
-
可用于用于存放私有持久文件。
-
非常適合用于存放app各種伴隨app運行周期所需要的文件數據,它既不會因為手機存儲空間不足而被清理,也不會因卸載app而遺留數據垃圾,并且它是私有的。
-
-
getDir(String name,int mode)
-
歸類存放私有文件。
-
在內部私有目錄下會創建一個名為app_name的文件夾,mode以前是可以設置文件夾私有(MODE_PRIVATE)和公有的(MODE_WORLD_READABLE、MODE_WORLD_WRITEABLE),但目前公有的mode都已經廢棄,意味著這個api創建的文件夾已經完全私有,不能再共享出去了。
-
外部存儲
在Android Q之前其他應用是可以訪問修改外部存儲的應用私有目錄的,這個要注意。
使用外部存儲之前一定要檢查外部存儲是否可用,因為舊設備不一定會有外部存儲,新手機也不一定會給你讀寫權限,就算用戶不給你權限,你的app也要運行啊,不然就不用你的了。
public static boolean isSDCardEnable {
return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState);
}
-
getExternalCacheDir
-
專門用于存放緩存數據。和內部存儲的getCacheDir相似。
-
-
getExternalFilesDir(String type)
-
歸類存放公有文件。
-
如果type不為的話在外部私有目錄下創建返回一個名為type的文件夾,為直接返回外部私有根目錄。如無特別需要,個人的做法是傳入Environment的DIRECTORY常量進行文件夾創建。
-
如果看完這篇你還不會選用api,那就把你應用雜七雜八的東西都放進去吧,文件至少不用東一件西一件的,卸載之后也能夠被正確清理掉,不過也要控制好占用的存儲空間。
-
-
getExternalMediaDirs
-
可存放共享媒體文件。
-
這個是在Android 5.0加入的api,創建和獲取位于/sdcard/Android/media目錄下的應用目錄,該目錄下的文件能夠被其他應用訪問和被MediaStore查詢和獲取。但目前較少開發者在使用這個api。
-
公有目錄
獲取公有目錄要使用Environment的Api,它返回的目錄全都是共享的公有目錄。造成Android手機文件存儲混亂的罪魁禍首!為數眾多的無責任開發者在這里胡亂創建文件夾,亂起名、亂放文件,普通用戶根本無法判斷哪些文件夾、文件是有用的,卸載app之后留下龐大的無法清理的垃圾文件,導致手機空間不足。于是它們在Android Q被廢棄了,但是Q之前還是能好好使用的,我認為要開始減少使用它們,更多地使用Context下的私有目錄API。
-
getExternalStorageDirectory
-
獲取外部存儲(SD卡)的根目錄。使用getExternalStoragePublicDirectory(String)進行替代即可。
-
-
getExternalStoragePublicDirectory(String type)
-
使用頻率極高的api,返回在根目錄下的名為type的文件夾,我把它分為兩種用法:一種是傳入Environment的DIRECTORY常量再創建子目錄使用;一種是傳入appPackageName或者易被識別歸屬的名稱創建子目錄使用。前者會比較通用,內容可以被各種工具app搜索發現(包括微信);后者算是私用,可以存放不跟隨app生命的文件,即卸載后也可以保留。
-
Environment.DIRECTORY_DCIM是手機的相冊,這個文件夾都是系統相關的app在用,存放相機拍攝的圖片,手機截圖之類的,不推薦開發者使用這個文件夾,避免混亂。值得一提的是淘寶有在使用這個文件夾,用于保存它的商品分享截圖,這個位置的確可以避免被微信封殺~哈哈
-
Environment.DIRECTORY_PICTURES用于存放各種“正式的”圖片,強烈建議在這里創建文件夾存放你想要被用戶發現的圖片,并且微信會掃描這個文件夾,讓你的圖片更容易分享。不過還。
-
Environment.DIRECTORY_DOWNLOADS可以用于存放app更新的apk等下載資源。
-
其他幾個比較少用就不介紹了。
-
適配Android Q
上面提到Environment的兩個公有目錄常用API在Android Q被廢棄了,應用存儲功能沙箱化,文件存放到沙箱外面要使用 DocumentFile,共享媒體文件要使用MediaStore進行,詳細的適配已有其他開發者分享出來了,推薦一下這篇:Android Q 存儲機制大變化(https://link.juejin.im/?target=https%3A%2F%2Ffeng.moe%2Farchives%2F47%2F)
我很喜歡的承香墨影也出了篇適配指南,內容更加詳細:Android Q 要來了,給你一份很"全面"的適配指南?。?em>https://mp.weixin.qq.com/s/aiDMyAfAZvaYIHuIMLAlcg)
結尾
最后說一下幾個重要的事:
獲取文件路徑這件事永遠不能寫死某個路徑,不存在SD卡怎么辦呢?某個路徑無法使用了怎么辦呢?所以管理文件的時候必須要有存儲策略。比如一個文件的保存地址獲取方法里不能只有一個api,要保有兜底措施,如果我不能存在外部儲存,那我就存在內部,保證app的功能正常運行。
這次的這里,希望看完這篇文章之后能夠讓你更了解如何管理手機文件夾。