1. 程式人生 > >解析Android內部儲存、外部儲存的區別

解析Android內部儲存、外部儲存的區別

1、背景

在開發過程中我們都會使用到手機的內部快取、外部快取。但有些開發者對這兩個儲存區域理解還夠透徹,以為手機內建的儲存卡(不可手機移除)就是內部儲存,
可插拔的SD卡就是外部儲存,其實這些理解都是有誤的。這個知識點本人也重複看過好幾次,但每次看完,過一段時間就會忘記,於是打算對這一知識點做個總結,也可當成學習筆記分享給大家。
主要分為下面兩點進行分析:

  • Android裝置檔案系統的目錄結構
  • 內部儲存和外部儲存的區別

2、 Android檔案系統目錄結構

很多同學都使用Window系統,對Window檔案系統的目錄結構也比較熟悉。Window使用的檔案系統是NTFS,NTFS檔案系統可把一個物理機械硬碟劃分為四個邏輯分割槽,每個分割槽對應一個碟符,如:C盤、D盤、E盤、F盤。
分割槽的目的是為了便於管理檔案。每個碟符在使用者看來是相互獨立的四塊磁碟,每個碟符都是一個根節點,如下圖所示:
這裡寫圖片描述

Android系統的核心使用的是Linux核心, 所以Android的檔案目錄結構和Linux系統的檔案目錄結構類似,Linux系統和Window系統屬於不同的作業系統,所以它們所使用的檔案系統也是不一樣的,
那對磁碟的管理方式也是不一樣的,這就造成了Android裝置的檔案目錄結構和Window系統裝置的檔案目錄結構不一樣的原因。
現在我們就來大致瞭解一下Android檔案系統的目錄結構,以及幾個重要的檔案目錄。
Android系統使用虛擬檔案系統(VFS), VFS的目錄是以/為根節點,根節點下又有不同的節點。而我們的物理儲存裝置就是掛載都這些節點上,如下圖所示:
這裡寫圖片描述

  1. /data/data/ apk的安裝目錄。 如:百度地圖的安裝路徑是/data/data/com.baidu.com/ 注意:該目錄需要獲取root許可權才能檢視
  2. /system/ 存放系統應用的apk檔案,即手機廠商預安裝應用的apk檔案 (手機廠商只需把需要預安裝的apk放在該節點的相應路徑下,android系統就會自己解壓並安裝該apk)
  3. /storage/ 該節點是內建儲存卡和外接SD卡的掛載點,/storage/emulated/0/是內建儲存卡掛載點, /storage/sdcard1是外接SD卡掛載點(不同的裝置掛載節點不一樣,有些裝置可能會掛載到/mnt/節點)。

3、內部儲存和外部儲存的區別

一般來說,現在的國產手機都會有兩個儲存裝置,一個就是內建的手機裝置中不可手動拆卸的儲存卡,我們稱之為內建儲存卡,另一個是可手動插拔外接儲存卡,我們稱之為外接SD卡。
對於Android開發者來說只和外部儲存和內部儲存打交道,Android提供的開發介面也只是獲取內部儲存和外部儲存的目錄地址。而對開發者遮蔽了內建儲存卡和外接SD卡。
所以很多同學都的理解就是:內建儲存卡是內部儲存, 外接SD卡 是外部儲存。 這樣的理解是不正確的。

以下是我個人的理解:
內建儲存卡 和外接SD卡 是不同物理儲存裝置上的劃分,一個是內建到裝置上,一個是在插在SD卡卡槽上。
而內部儲存 和外部儲存 以是否是應用的安裝目錄來劃分,內部儲存 是在應用的安裝目錄下,外部儲存 在應用的安裝目錄外。
一個劃分是物理上的劃分,一個是邏輯上的劃分,所以內建儲存卡 不是內部儲存 ,外接SD卡 也不是外部儲存 。

  • 內部儲存:
    /data/data/目錄下都是已安裝的應用程式的安裝目錄, 該目錄下包含的檔案都是以包名作為檔名的目錄,如我測試demo的安裝目錄為:/data/data/com.bgl.storage/。
    內部儲存的檔案是應用的私有檔案,其他應用(和使用者)不能訪問這些檔案。每個應用訪問自己的內部儲存是不需要許可權的。 當用戶解除安裝應用時,這些檔案也會被移除。
    context.getDir(String name, String mode)可建立並返回一個內部儲存的檔案,mode用於指示檔案的建立模式,指定為MODE_PRIVATE 將會把檔案設為應用的私有檔案。
    其他可用模式包括:MODE_APPEND、MODE_WORLD_READABLE和 MODE_WORLD_WRITEABLE。

注:自 API 級別 17 以來,常量 MODE_WORLD_READABLE 和 MODE_WORLD_WRITEABLE 已被棄用。從 Android N 開始,
使用這些常量將會導致引發 SecurityException。這意味著,面向 Android N 和更高版本的應用無法按名稱共享私有檔案,
嘗試共享“file://”URI 將會導致引發FileUriExposedException。 如果您的應用需要與其他應用共享私有檔案,則可以將 FileProvider 與 FLAG_GRANT_READ_URI_PERMISSION 配合使用。

獲取內部儲存地址的方式如下:
這裡寫圖片描述

  • 外部儲存:

外部儲存可以是外接SD卡 ,也可以是內建儲存卡 的部分分割槽。 外部儲存可分為公共目錄和私有目錄
在Android4.4以前讀取或寫入外部儲存(包括公共目錄和私有目錄)的檔案,必須獲取 READ_EXTERNAL_STORAGE 或 WRITE_EXTERNAL_STORAGE 系統許可權。 如下:

  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

但從Android 4.4 開始,操作私有目錄不再需要 READ_EXTERNAL_STORAGE 或 WRITE_EXTERNAL_STORAGE 許可權。
因此如果我們的應用只使用到外部儲存中的私有目錄的話,可通過新增 maxSdkVersion 屬性來宣告,只需在較低版本的 Android 中請求該許可權:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"   android:maxSdkVersion="18" />

注意:如果操作外部儲存的公共目錄,我們還是需要申請 READ_EXTERNAL_STORAGE 或 WRITE_EXTERNAL_STORAGE 許可權。

私有目錄屬於應用私有,但這些私有資料是可以被其它應用訪問和修改的(前提是知道應用私有目錄的地址)。
當用戶解除安裝應用時,此目錄及其內容將被刪除。此外,系統媒體掃描程式不會讀取這些目錄中的檔案,因此不能從 MediaStore 內容提供程式訪問這些檔案。
因此,如果你正在處理的檔案不適合其他應用使用(例如僅供您的應用使用的圖形紋理或音效),則應該儲存在外部儲存上的私有儲存目錄。
私有目錄 的獲取方式如下:
這裡寫圖片描述
注意:如果使用者在計算機上裝載了外部儲存或移除了介質,則外部儲存可能變為不可用狀態。所有應用都能讀取和寫入放置在外部儲存上的檔案,並且使用者可以移除這些檔案。

在使用外部儲存執行任何工作之前,應始終呼叫 getExternalStorageState() 以檢查介質是否可用。介質可能已裝載到計算機,處於缺失、只讀或其他某種狀態。
例如,以下是可用於檢查可用性的幾種方法:

/* Checks if external storage is available for read and write */
public boolean isExternalStorageWritable() {
    String state = Environment.getExternalStorageState();
    if (Environment.MEDIA_MOUNTED.equals(state)) {
        return true;
    }
    return false;
}

/* Checks if external storage is available to at least read */
public boolean isExternalStorageReadable() {
    String state = Environment.getExternalStorageState();
    if (Environment.MEDIA_MOUNTED.equals(state) ||
        Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
        return true;
    }
    return false;
}

有些裝置支援外插SD卡,所以這類裝置的外部儲存由內建儲存卡分割槽和外接SD卡組成:
在Android4.3及以下的系統,只能通過Context.getExternalFilesDir(type)來獲取外部儲存在內建儲存卡分割槽上的私有目錄地址,而無法獲取到外部儲存在外接SD卡上的地址。
從Android4.4開始,你可以通過Context.getExternalFilesDirs(type)獲取一個File陣列,File陣列中就包含了內建儲存卡分割槽和外接SD卡的私有目錄地址。
如果您希望在支援 Android 4.3 和更低版本的同時訪問兩個可能的位置,請使用相容庫中的靜態方法 ContextCompat.getExternalFilesDirs()。

4、總結

  • Android系統使用的是虛擬檔案系統(VFS),VFS提供了供儲存裝置掛載的節點。同一個儲存裝置經過分割槽後,不同的分割槽可以掛載到不同的節點上,如手機的內建儲存卡。
  • 使用內部儲存是不需要許可權的,內部儲存屬於應用的私有儲存區域,其它應用不可訪問,當應用被解除安裝時,內部儲存中的檔案也會被刪除。
  • 外部儲存分為公共目錄和私有目錄,外部儲存是可以全域性訪問的,但需要申請許可權:
    1. Android4.4以前訪問私有目錄和公共目錄都需要申請 WRITE_EXTERNAL_STORAGE 許可權
    2. Android4.4以後訪問私有目錄不需要申請許可權,但公共目錄是需要申請 WRITE_EXTERNAL_STORAGE 許可權
  • 自 API 級別 17 以來,常量 MODE_WORLD_READABLE 和 MODE_WORLD_WRITEABLE 已被棄用。從 Android N 開始,使用這些常量將會導致引發 SecurityException。
  • 如果快取的資料量比較大,請不要儲存到記憶體儲存中,如果想儲存可共享給其它應用的資料,請儲存到外部儲存的公共目錄中。
  • 設定項的Clear Data 和Clear cache兩個選項,這兩個都是情況應用的快取資料,具體區別如下:
    1. Clear Data清理的是外部儲存中的應用私有目錄下的file資料夾
    2. Clear Cache清理的是外部儲存中的應用私有目錄下的cache資料夾