1. 程式人生 > >Android 各種路徑詳細說明

Android 各種路徑詳細說明

公有 4.2 查找 知識 移動 禁用 符號 alc 絕對路徑

說起Android“內部存儲”,對於開發者而言可以毫不猶豫地脫口而出,不就是/data/data目錄嘛,也不盡然,/data/user/0/又如何解釋?那說起“外部存儲”,例如sd卡目錄獲取值,更是眾說紛紜,是/mnt/sdcard?還是/storage/sdcard0?莫非是/storage/emulated/0? 此疑問起源要追溯到筆者在寫上一篇博文,即以DexClassLoader類加載原理編寫demo實現類替換修復,其中編碼過程中涉及到讀寫Android內外存儲的文件數據,邏輯就是讀取外部存儲sd卡上的dex文件,寫入到內部存儲對應的app包下的一個自定義目錄中。 以上邏輯並不復雜,但是筆者用模擬器和不同版本的Android真機測試時,發現這獲取sd卡的路徑卻各不相同,思索不解一個百度,發現其中大有文章,有的目錄只是鏈接,是個軟連接,而大部分原因需歸咎於Android版本更新中“多用戶”的新特性出現,Google對用戶的數據結構進行了調整。可具體實情如何,請看以下解說: 本篇博文涉及到的知識點如下: Android內、外部存儲基礎知識點及文件夾所在位置 存儲路徑中“0”的含義與變化 多種sd卡路徑表示含義與區別 一. Android內外存儲基礎知識 Android手機上的存儲空間可做如下劃分: 內存:RAM 內部存儲:內部ROM 外部存儲:外部ROM和SDCard 手機上的存儲在概念上分成了”內部internal“和”外部external“兩部分,但其實都在手機內部。因此無論Android手機是否有可移動的sdcard,它都有外部存儲和內部存儲,且通過相同的Api方法來訪問可移動的sdcard或者手機自帶的存儲。 以下是一部SamSung手機的存儲展示圖,用AS自帶的Device File Explore打開後展現的文件目錄。 (1)內部存儲: Android可以說是一個Linux操作系統,它的內部存儲空間對於應用程序和用戶來講就是“/data/data“目錄。內部存儲與外部存儲相比有著比較穩定,存儲方便,操作簡單,更加安全(可以控制訪問權限)等優點,而它唯一的缺點就是空間有限。 內部存儲空間的有限意味著應物盡其用,用來保存比較重要的數據,例如用戶信息資料,口令秘碼等不需要與其他應用程序共享的數據。註意應用程序被卸載時,應用程序在內部存儲空間的文件數據將全部被刪除,避免占用寶貴的空間。 內部存儲即data文件夾,其中裏面有兩個文件夾值得關註: app文件夾(未root無法打開):存放著所有app的apk文件夾,當開發者調試某個app時,AS控制臺輸出的內容中有一項是uploading…,代表正在上傳apk到這個文件夾。 data文件夾:內部都是app的包名,存儲著應用程序相關的數據,例如 data/data/包名/(shared_prefs、database、files、cache) (2)外部存儲 外部存儲是指用戶在使用時自行在手機上添加的外部存儲介質,例如TS卡,SD卡等閃存儲介質。其顯著的優點就是存儲空間大,無需擔心數據清除問題,與內部存儲不同的是當應用程序卸載時,它在外部存儲所創建的文件數據不會被清除,因此清理外部存儲空間的責任丟給了用戶自己。缺點則是不太穩定,閃存介質對於Android手機而言會出現SD卡不能正常使用的情況。 外部存儲即storage文件夾或mnt文件夾。需要註意的是storage中有一個sdcard0文件夾,其中又分為公有目錄和私有目錄: 公有目錄:有9大類,比如DCIM、Download等系統為用戶創建的文件夾; 私有目錄: 即Android文件夾/storage/sdcard/Android/,其中的data文件夾包含了許多包名組成的文件夾。 若應用程序在運行過程中需要向手機上保存數據,通常是保存在sdcard中/storage/sdcard,即應用直接在sdcard的根目錄創建一個文件夾用於數據保存,不過當該app被卸載後,數據還保留在sdcard中,意味著留下了垃圾數據。 在開發中,Google官方建議App數據存儲在外部存儲的私有目錄中對應App的包名下storage/sdcard/Android/data/包名/,這樣當用戶卸載掉App之後,相關的數據會一並刪除! (3)內外部存儲常用目錄操作 context.getFilesDir() 內部存儲data/data/包名/files目錄 context.getCacheDir() 內部存儲data/data/包名/cache目錄 Environment.getExternalStorageDirectory() 外部存儲根目錄 Environment.getExternalStoragePublicDirectory (Environment.DIRECTORY_DCIM) 外部存儲公有目錄 context.getExternalFilesDir() 外部存儲私有目錄storage/sdcard/Android/data/包名/files。一般存儲長時間保存的數據。 context.getExternalCacheDir() 外部存儲私有目錄storage/sdcard/Android/data/包名/cache。一般存儲臨時緩存數據。 註意上述最後兩個API:當app被卸載後,sdCard/Android/data/PackageName/下的所有文件都會被刪除,不會留下垃圾信息。兩個API對應的目錄分別對應著 設置->應用->應用詳情裏面的“清除數據”與“清除緩存”選項。 二. 各個易混淆路徑識別 1. 文件夾中0的出現 在Android版本4.2JellyBean之前,獲取sdcard的路徑是/sdcard/,但在JellyBean版本之後的路徑成為了/sdcard/0,或者是/sdcard/legacy(legacy可以是0、1、2……),這個“0”到底代表著什麽含義? 這是從JellyBean版本起的一個新特征——多用戶。因此為了處理單獨的賬戶,部分目錄結構必須被改變,/sdcard/legacy始終指向當前登錄的用戶的SD卡目錄。 正因“多用戶”功能的增加,內外部存儲發生了以下變化: 內部存儲: 原先的/data/data/其實相當於直接鏈接到當前用戶文件夾的,變成了/data/user/0/。 外部存儲:例如sd卡路徑不再是/sdcard/,而是/sdcard/legacy/(legacy可以是0、1、2……),其中的“0”可以當成“設備擁有者”,或者稱為“第一用戶”(“第一用戶”毫無疑問的是“設備所有者”,只有此用戶才能創建額外賬戶)。 大致理解了其由來後,可能少數人還遇到過/sdcard/0/0 這種表示,以下是谷歌對此的官方解釋: Google在Android 4.2中引入了“多個用戶”作為新功能, 為了適應“多個用戶”,Google現在為每個用戶提供了自己的文件夾以供存儲。 如果您從4.1升級到4.2,則4.2 ROM將在/data中查找某個文件,以確定是否需要將所有文件遷移到新的多用戶數據結構。 默認情況下,4.2將所有/data/media遷移到/data/media/0。不過使用自定義恢復會出現問題,自定義恢復在出廠重置期間會保留/data/media文件夾。當您重新設置工廠並再次啟動4.2 ROM時,4.2 ROM將再次遷移/data/media中的所有內容。 每次出廠重置時它都會遷移您的文件,這種多次遷移會導致一些人將他們的文件移動到/sdcard/0甚至/sdcard/0/0等等。 2. sd卡路徑隨著Android版本變化 public static final String SDPATH = Environment .getExternalStorageDirectory().getAbsolutePath();//獲取外部存儲的路徑返回絕對路徑的,就是你的設備SD卡的文件路徑 1 如上是獲取SD卡目錄的Api,你會發現使用不同Android移動設備得到的sd卡目錄不同,不僅如此,其中路徑指向的都是相同的文件,緣由為何? /sdcard/ /mnt/sdcard/ /storage/sdcard0/ /storage/emulated/legacy/ 1 2 3 4 上面四個皆指代sd卡路徑,其中演變的由來見以下Android版本分享: (1)android 4.0 Galaxy Nexus(GN)手機上的userData分區很大,被掛在/data 目錄下。用戶的數據通常是存儲在SD卡上,可是GN無SD卡,即只有intenal內部存儲。(第四點中會講解Galaxy Nexus為何只有內部存儲緣由) Google爸爸並未被眼前困難打倒,直接在userData分區下虛擬了一個media目錄,它是是內置sd卡的數據存儲位置,具體使用了fuse技術將/data/media虛擬成為一個叫做/dev/fuse的設備,為了讓程序能認出來,被同時掛載在 /mnt/sdcard 目錄,又為了兼容以前的程序,做了一個快捷方式(linux系統裏叫軟連接),因此 /sdcard 指向的就是 /mnt/sdcard。 (2)android 4.1 以上是Android 4.0的應對措施,在4.1中同樣使用fuse技術,/dev/fuse 會被同時掛載到/storage/sdcard0 目錄。“sdcard0”表示第一個sd卡(若有外置sd卡,那會多一個 /storage/sdcard1) /sdcard 軟連接會指向 /storage/sdcard0 ,此時/mnt/sdcard 也是個軟連接,會指向/storage/sdcard0。 因此/storage/sdcard0才是最終源頭,/sdcard、/mnt/sdcard是指向它的一個軟連接而已。 如果你通過otg線接U盤,會被掛載到 /storage/usb0目錄,stickmount這個軟件為了讓圖庫、快圖、mx player等軟件,能看到u盤裏的數據,又同時掛載到 /storage/sdcard0/usStorage/sda1。為何命名為“usb0”,而不是“sda1”? 這是Linux的對硬盤的命名方式,如果你的u盤有多個分區,就分別是按sda1、sda2此規律命名下去。 (3)android 4.2及之後 在4.2之後,Android出現了一個“多用戶”的功能特征,意味著一臺設備可能被多個人同時應用,因此需要將每個人的數據、應用、個性配置分開。後兩者容易實現,通過權限控制即可,可是數據這一塊如何是好? 面對需求的變更,只好再調整用戶數據的掛載結構,4.2版本中同樣使用fuse技術,而/dev/fuse 會被掛載到/storage/emulated/0 目錄。(若有多個用戶,0文件夾名稱依次增加,例如/storage/emulated/1) 不僅如此,為了兼容Android版本升級,還同時掛載到/storage/emulated/legacy,建立了三個軟連接指向自己: /storage/sdcard0 /sdcard /mnt/sdcard (4)不要太在意“0” 也許在手機設備開發中似乎沒太註意到“多用戶”新特性的作用,無需太過在意,它主要在平板上使用,手機端是禁用的,但底層實現原理相同。 /mnt/shell/emulated 目錄和 /storage/emulated 下的文件夾是一樣的。 (此點“版本總結”來源於機鋒論壇之關於android的4.2的0文件夾的詳解,筆者稍有修改) 3. 多種sd卡路徑表示的區別總結 在上一點中了解了表示sd卡幾種路徑的演變過程,此部分對以下幾個路徑進行簡單總結區別: (註意:下面路徑中的文件夾相同) /sdcard/ /storage/sdcard0/ /storage/emulated/0/ /storage/emulated/legacy/ 1 2 3 4 /sdcard/:只是一個符號鏈接,鏈接到/storage/sdcard0/ /mnt/sdcard:Android4.0版本之前的顯示 /storage/sdcard0 :Android4.0版本之後的顯示 /storage/emulated/0/:這是參照“emulated MMC”,通常指其內部,“0”代表第一個用戶,即設備擁有者。 如果您創建其他用戶,則此數字將為每個用戶增加。 /storage/emulated/legacy/:與上同理,但指向當前工作用戶的部分。(對於“0”用戶而言,這是/storage/emulated/0/的符號鏈接) /storage/sdcard0/:註意這裏的“0”並非是一個單獨文件夾名,而是作為後綴一樣,意味著“0”並不代表用戶,而是設備(卡)本身,因此它不需要legacy鏈接。人們可以通過OTG將讀卡器與另一個SD卡連接起來,然後路徑將成為/storage/sdcard1/。 為了兼容Android版本升級,還同時掛載到/storage/emulated/legacy,建立了三個軟連接指向自己:/storage/sdcard0 、/sdcard、/mnt/sdcard。 sdcard0名稱變化 “sdcard”這個文件夾是手機外置SD卡的文件夾名稱,“sdcard0”這個文件夾是手機內存文件夾名稱。手機內存本身是無法擴展的,但是外置SD卡的空間用戶可以根據手機最大使用限制購買更換。因此“sdcard0”是內置儲存,“sdcard1”是擴展儲存,就是一般的sd卡。 如果更多的設備連接,但命名可能會有所不同,取決於設備: 當內部虛擬SD卡:/mnt/sdcard0/ SD插槽中的物理SD:/mnt/sdcard1/ 可調用其他設備,如連接適配器的USB閃存驅動器:/mnt/media_rw/usbdisk(kitkat 4.4及以上?)或/mnt/usbdisk(jellybean 4.1 - 4.2) 命名可能取決於設備、制造商、 ROM等, 但通常情況下,/mnt/*在所有這些情況下只是一個符號鏈接,而源頭是位於/storage路徑下。 (此點回答來自於StackExchange,結合自身理解翻譯敘述) 4. “/sdcard/”是一個“假”路徑? 上一點解決了各個路徑的識別問題,可仍剩下一個疑惑:Glaxy Nexus沒有用於SD卡的外部插槽,即只有內部存儲,但它仍有一個名為/sdcard/的根文件夾(第二點中的(1)部分已簡單解釋了原因),詳情為何?以下是Google工程師回應Galaxy Nexus背後缺乏USB大容量存儲的理由,即Android為什麽從USB大容量存儲轉移到內部存儲的MTP訪問的解釋,可以揭秘為何GN中/sdcard/“假”路徑的問題。 UMS:通信技術; MTP:multi-path transmission網絡傳輸機制,多路徑傳輸是指采用多條不相交的路徑來投遞應用分組以增加連接的容量和可靠性的機制; ICS:Internet連接共享 Galaxy Nexus不支持USB海量存儲(UMS),即不支持USB存儲模式,這給許多用戶帶來了震撼。 所有擁有XOOM,Nexus S,Galaxy Tab 10.1或任何缺少SD卡插槽的設備的人都熟悉這種設置,因為上述所有設備的工作方式與Galaxy Nexus類似,都是使用MTP網絡傳輸機制而不是UMS通信技術。以下Google工程師回應了Galaxy Nexus相關問題: Q: ICS不支持USB大容量存儲? A: ICS支持USB海量存儲(UMS), Galaxy Nexus不會,這與Honeycomb(蜂巢)相同,例如HC支持USB大容量存儲,而Xoom不支持。 如果給定設備有可移動的SD卡,它將支持USB大容量存儲。如果設備只有內置存儲(如Xoom、Galaxy Nexus),它通常僅支持MTP和PTP。在沒有專用存儲分區的設備上(如可移動SD卡或像Nexus S這樣的單獨分區),在物理上不可能支持UMS。這是因為UMS是塊級協議,主機PC直接訪問存儲上的物理塊,以便Android不能同時安裝它。 通過我們在Honeycomb中引入的統一存儲模型,可以在應用程序數據和媒體數據之間分享完整的32GB(或16GB其他),即當您的內部應用數據分區已滿時,Nexus S上的5GB免費空間不會再無所事事。然而這樣的代價是,Android不能再為主PC直接通過USB傳輸存儲。相反使用MTP。在Windows(大多數用戶使用)上,它在資源管理器中內置了MTP支持,使其看起來完全像磁盤。(Linus和Mac上沒那麽容易,sad) 總體而言,這是一個更好的手機體驗。 Q: 由於Galaxy Nexus僅具有內部存儲,因此ASTRO文件管理器等應用程序如何在不需要root權限的情況下工作? A:首先我們在內部存儲器上指定一個特定的目錄作為“SD卡”, 然後實現了一個FUSE文件系統,除了丟棄所有權限檢查之外,什麽也不做,除非重新將該目錄掛載為/sdcard。 除了權限之外,FUSE文件系統是直通的,因此實際的文件可以從目錄中讀取或寫入目錄。 我們使用“假代理” FUSE文件系統來重新安裝特定的目錄以偽裝成SD卡, 這對於應用程序來說是完全透明的,他們不能說沒有直接與磁盤通話。 (閱讀自此,GN中為何有/sdcard/ 這個“假”路徑的秘密已經被揭開,下一個Q&A則是幫助開發者更深入理解Glaxy Nexus為何舍棄USB大容量存儲,他們考慮的思路又是如何?) Q: Galaxy Nexus這麽做也許有它的理由,可是Micro-SD卡插槽需要多少空間?為何不可兩者兼得? A:舍棄SD卡插槽主要原因並不在硬件上,而是沒有一個適合的使用UI。Android核心原則之一是你永遠不需要文件管理器,因此我們想要避開其他操作系統的一個綜合癥——文件選擇器。app應該知道如何處理它的本地數據,即存儲在本地或者雲端,而不是搜查你的Micro-SD卡來獲取數據。 同時擁有內部存儲和SD卡的這個目標確實有些難以實現,例如指定的相片,相機應該保存在內部的16GB還是SD卡?市場中的app應該安裝到內部存儲還是SD?諸如此類等等。沒錯,你也許會說一個簡單的選擇或者設置即可解決,但那樣類似於文件選擇器的體驗並非是我們的理想期望。除此缺點之外,還要考慮到API,若用戶在SD卡上存儲照片,是否將其添加到系統媒體內容提供商?如果需要的話這對app而言是個災難,因為他們沒有操作照片來去這種概念。 而我們最終可能做的是將“導入/導出”這種概念添加到可移動存儲。因此相機將始終保存到內部存儲中,並且當您彈出SD卡時(或在USB主機設備上插入拇指驅動器),您可以啟動“”遷移或“導入/導出”對話框。 (此點Q&A來源於androidpolice新聞,結合自身理解翻譯敘述) Android開發中有一個隱性的問題,就是市面上各個手機的Android版本並不統一。會被不同版本所影響的邏輯處理,開發人員需要做好兼容處理,去適應市場上大部分機型,例如此篇文章的混淆點。 此篇文章主要的疑問通過搜索多篇文章最終解決,其中涉及到不少英文網站,例如StackOverflow、Stack Exchange,還有國內的論壇、博客等等,知識開源的感覺還是很棒的,雖然國內還是偏少,共勉~ 若翻譯、理解有誤,虛心指教~ --------------------- 本文來自 lemonGuo 的CSDN 博客 ,全文地址請點擊:https://blog.csdn.net/ITermeng/article/details/79423035?utm_source=copy

Android 各種路徑詳細說明