Android設備的內置存儲和外置存儲到底是怎麼回事,深入理解

在所有的Android手機中,文件存儲空間都分爲兩部分:①手機內置(internal)的,不可卸載的;②外置(external)的可卸載的SD卡。不過隨着手機的發展,有些廠商生產的設備將“internal”和“external”都做成了不可卸載的內置存儲。在實際開發中文件到底應該存儲到哪裏,怎麼存儲經常會困擾我們,筆者爲了徹底弄清楚這個問題,就查看了Android官方網站的相關資料,並翻譯如下(有譯的不好的請指教),爲了便於大家閱讀,將重要的原文段落附上:

Choose Internal or External Storage(選擇內置存儲還是外置存儲)

All Android devices have two file storage areas: "internal" and "external" storage. These names come from the early days of Android, when most devices offered built-in non-volatile memory (internal storage), plus a removable storage medium such as a micro SD card (external storage). Some devices divide the permanent storage space into "internal" and "external" partitions, so even without a removable storage medium, there are always two storage spaces and the API behavior is the same whether the external storage is removable or not. The following lists summarize the facts about each storage space.

譯:所有的 Android 設備都有兩個文件存儲區域:內置的(internal)和外置的(external)存儲區域。這兩個名稱來源於早期版本的Android系統,當時大多數設備提供了 內置的不可變的內存(internal storage),外加一個可卸載的外部存儲媒介(external storage),就像SD卡這樣的媒介。之後一些設備提供了永久的存儲空間並將其分爲 “internal” 和 “external” 兩部分,雖然沒有了可拆卸的存儲介質,但是存儲介質任然分爲兩個存儲空間,並且API 的行爲不會因爲外部存儲是否可拆卸而有所不同(意思就是API操作是相同的)。下面的列表總結了兩者之間的差異:

Internal storage:

  • It's always available.(總是可用的)
  • Files saved here are accessible by only your app by default.(這裏的文件默認只能被我們的 app所訪問)
  • When the user uninstalls your app, the system removes all your app's files from internal storage.(當用戶卸載app 的時候,系統會將internal內該app的文件清除乾淨)
  • Internal storage is best when you want to be sure that neither the user nor other apps can access your files.(當你想確保不管是用戶還是其他的app都無法訪問你的文件時存儲在internal是最好的)

External storage:

  • It's not always available, because the user can mount the external storage as USB storage and in some cases remove it from the device.(並不總是可用的,因爲在有些情景下用戶可以通過USB存儲模式掛載外部存儲器,此時便無法訪問了)
  • It's world-readable, so files saved here may be read outside of your control.(是大家都可以訪問的,所以文件存儲在這可以被其他程序訪問而不受你的控制)
  • When the user uninstalls your app, the system removes your app's files from here only if you save them in the directory from getExternalFilesDir().(當用戶卸載app時,系統僅僅會清除通過getExternalFilesDir().方法得到的根目錄下與app相關的文件)

External storage is the best place for files that don't require access restrictions and for files that you want to share with other apps or allow the user to access with a computer.(外部存儲是存儲不需要訪問限制條件的文件以及你想與其他app共享文件或者允許用戶通過電腦訪問文件的最佳存儲區域)
























提示:儘管應用程序(app)默認安裝在內置存儲中,你可以在清單文件(mainifest.xml)中指定android:installLocation 屬性來將我們的app安裝到外置存儲(external storage)中。當我們的apk文件過大,而且外置存儲又比內置存儲大時,這個選項會對我們非常有用。

Obtain Permissions for External Storage(獲取外部存儲權限)

To write to the external storage, you must request the WRITE_EXTERNAL_STORAGE permission in your manifest file:

譯:爲寫數據到外置存儲,我們必須清單文件(manifest)中請求WRITE_EXTERNAL_STORAGE 權限。

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

Caution: Currently, all apps have the ability to read the external storage without a special permission. However, this will change in a future release. If your app needs to read the external storage (but not write to it), then you will need to declare the READ_EXTERNAL_STORAGE permission. To ensure that your app continues to work as expected, you should declare this permission now, before the change takes effect.

譯:注意:當前,所有app擁有讀取external  storage的能力而不需要指定權限。然而,這將會將來發布的版本中有所改變。如果我們的app需要讀取external  storage(但是不去寫入數據時),這個時候你需要聲明READ_EXTERNAL_STORAGE 這個權限。爲了確保你的app能夠持續的正常運行,在未來的版本修改生效之前,你還是應該聲明這個讀權限。

<manifest ...>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    ...
</manifest>
然而,如果你的app使用了WRITE_EXTERNAL_STORAGE這個權限,它同時也暗示了你擁有讀外置存儲的 權限。

對於保存文件到internal  storage,你不需要任何權限。我們的應用程序擁有在內置存儲目錄下讀寫文件的權利。

Save a File on Internal Storage(保存文件到Interanl Storage)

當保存文件到Internal Storage時,可以通過執行下面兩個方法之一來獲取合適的目錄最爲FILE 對象:

getFilesDir() Returns a File representing an internal directory for your app.(譯:返回一個代表了我們app的internal 目錄) getCacheDir() Returns a File representing an internal directory for your app's temporary cache files. Be sure to delete each file once it is no longer needed and implement a reasonable size limit for the amount of memory you use at any given time, such as 1MB. If the system begins running low on storage, it may delete your cache files without warning.(譯:返回一個代表我們app的在internal裏的臨時緩存目錄。請確保這個目錄下的文件一旦你不再需要時馬上刪除,並根據你在某個時間要使用的存儲空間大小設置一個合理的限制,比如1M。如果系統開始運行緩慢,它會刪除我們的緩存文件,連一個警告都不會有)To create a new file in one of these directories, you can use the File() constructor, passing the File provided by one of the above methods that specifies your internal storage directory. For example:(譯:想在這其中的一個目錄中創建一個新的文件,你可以使用  File() 構造器,傳入的 File 參數由上面的某一個方法指定的內置存儲目錄。例如:)

File file = new File(context.getFilesDir(), filename);
Alternatively, you can call openFileOutput() to get a FileOutputStream that writes to a file in your internal directory. For example, here's how to write some text to a file:(二者擇一的,你可以調用openFileOutput()方法去獲取一個  FileOutputStream 對象,這個對象寫數據到internal  directory的一個文件,例如,如何寫一些文本到一個文件:
String filename = "myfile";
String string = "Hello world!";
FileOutputStream outputStream;

try {
  outputStream = openFileOutput(filename, Context.MODE_PRIVATE);
  outputStream.write(string.getBytes());
  outputStream.close();
} catch (Exception e) {
  e.printStackTrace();
}
Or, if you need to cache some files, you should instead use createTempFile(). For example, the following method extracts the file name from a URL and creates a file with that name in your app's internal cache directory:(譯:或者你想緩存一些文件,你應該使用 createTempFile() 方法來代替。例如,下面的方法從一個 URL 提取文件名 並且在你的app的內置緩存目錄中新建一個以這個名字爲文件名的文件):

<span style="color:#333333;">public File getTempFile(Context context, String url) {
    File file;
    try {
        String fileName = Uri.parse(url).getLastPathSegment();
        file = File.createTempFile(fileName, null, context.getCacheDir());
    catch (IOException e) {
        // Error while creating file
    }
    return file;
}</span>
Note: Your app's internal storage directory is specified by your app's package name in a special location of the Android file system. Technically, another app can read your internal files if you set the file mode to be readable. However, the other app would also need to know your app package name and file names. Other apps cannot browse your internal directories and do not have read or write access unless you explicitly set the files to be readable or writable. So as long as you use MODE_PRIVATE for your files on the internal storage, they are never accessible to other apps.
譯:註釋:你的app的內置存儲目錄在Android文件系統中的一個確定的位置並且由你的app 的包名來指定。從技術上講,如果你的文件設置爲可讀模式,其他的app可以讀取你的internal文件。然而,其他的app 需要知道你的app的包名以及文件名。其他的app 不能瀏覽你的internal 目錄,沒有讀寫訪問權限除非你明確設置文件是可讀或者可寫的。所以只要你爲你的存儲在internal  storage(內置存儲)中的文件使用  MODE_PRIVATE  模式,那麼它們對於其他的app永遠也不會有訪問權限。

Save a File on External Storage(保存文件到外置存儲)

Because the external storage may be unavailable—such as when the user has mounted the storage to a PC or has removed the SD card that provides the external storage—you should always verify that the volume is available before accessing it. You can query the external storage state by calling getExternalStorageState(). If the returned state is equal to MEDIA_MOUNTED, then you can read and write your files. For example, the following methods are useful to determine the storage availability:
譯:因爲外置存儲可能是不可用的:比如當用戶掛載存儲器到PC,或者移除了提供外置存儲的SD卡時,你應該在訪問它之前總是驗證它是否可用通過調用  getExternalStorageState() 方法。如果返回的狀態與 MEDIA_MOUNTED  相等,這時你可以讀和寫你的文件。例如:以下方法在確定存儲器是否可用是非常有用。

/* 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;
}
Although the external storage is modifiable by the user and other apps, there are two categories of files you might save here:(儘管外置存儲可能被用戶或者其他app改變,但是這裏仍有兩類文件你需要保存到這裏:)
Public files Files that should be freely available to other apps and to the user. When the user uninstalls your app, these files should remain available to the user.(這些文件對用戶和其他app都是可訪問的,當用戶卸載app的時候,這些文件任然存在)

For example, photos captured by your app or other downloaded files.(比如,我們的app捕捉到 圖片或者其他下載的 文件)

Private files Files that rightfully belong to your app and should be deleted when the user uninstalls your app. Although these files are technically accessible by the user and other apps because they are on the external storage, they are files that realistically don't provide value to the user outside your app. When the user uninstalls your app, the system deletes all files in your app's external private directory.(文件僅僅屬於你的app,並且當用戶卸載app的時候應該被刪除。儘管這些文件在技術上來說是可以被用戶或者第三方app訪問,因爲他們在外置存儲器上,這些文件只有在使用我們的app時纔會提供它的真實值。當用戶卸載app時,系統會刪除在app的私有目錄下的所有文件) For example, additional resources downloaded by your app or temporary media files.(比如,你的app下載的附件的資源以及臨時的媒體文件。)
If you want to save public files on the external storage, use the getExternalStoragePublicDirectory() method to get a File representing the appropriate directory on the external storage. The method takes an argument specifying the type of file you want to save so that they can be logically organized with other public files, such as DIRECTORY_MUSIC or DIRECTORY_PICTURES. For example:

譯:如果你想在外置存儲上保存公共的文件,使用  getExternalStoragePublicDirectory()  方法 得到一個 File 對象,它代表在外置存儲上的適當目錄。這個方法帶有一個指定你想保存的文件的類型的參數,這樣他們可以與其他公共的文件合乎邏輯的組織起來,諸如 DIRECTORY_MUSIC 和 DIRECTORY_PICTURES.例子:

public File getAlbumStorageDir(String albumName) {
    // Get the directory for the user's public pictures directory. 
    File file = new File(Environment.getExternalStoragePublicDirectory(
            Environment.DIRECTORY_PICTURES), albumName);
    if (!file.mkdirs()) {
        Log.e(LOG_TAG, "Directory not created");
    }
    return file;
}

If you want to save files that are private to your app, you can acquire the appropriate directory by callinggetExternalFilesDir() and passing it a name indicating the type of directory you'd like. Each directory created this way is added to a parent directory that encapsulates all your app's external storage files, which the system deletes when the user uninstalls your app.

For example, here's a method you can use to create a directory for an individual photo album:

譯:如果你想保存對你的app來說是私有的文件,你可以通過調用  getExternalFilesDir() 方法來獲取一個合適的目錄,並且傳遞給它一個表明你想要的目錄類型的名字。每一個通過這種方式創建的目錄被添加到一個父目錄,這個父目錄封裝了你的app 的所有外部存儲文件,而且這個目錄會在在用戶卸載app時被系統刪除。

例如:這裏有一個方法你可以用來爲一些獨特的相冊新建一個目錄:

public File getAlbumStorageDir(Context context, String albumName) {
    // Get the directory for the app's private pictures directory. 
    File file = new File(context.getExternalFilesDir(
            Environment.DIRECTORY_PICTURES), albumName);
    if (!file.mkdirs()) {
        Log.e(LOG_TAG, "Directory not created");
    }
    return file;
}

If none of the pre-defined sub-directory names suit your files, you can instead call getExternalFilesDir() and pass null. This returns the root directory for your app's private directory on the external storage.(譯:如果沒有預先定義適合你的文件的子目錄的名字,你可以代替調用 getExternalFilesDir() 方法並且傳一個null這時他會返回你的app在external storage下的private 的根目錄。 )

Remember that getExternalFilesDir() creates a directory inside a directory that is deleted when the user uninstalls your app. If the files you're saving should remain available after the user uninstalls your app—such as when your app is a camera and the user will want to keep the photos—you should instead usegetExternalStoragePublicDirectory().(譯:請記住,getExternalFilesDir() 方法創建的目錄在app被卸載時會被系統刪除。如果你保存的文件在用戶卸載app之後應該繼續存留下來,比如你的app是一個相機並且用戶希望保存拍下的照片,你應該使用方法getExternalStoragePublicDirectory()。)

Regardless of whether you use getExternalStoragePublicDirectory() for files that are shared orgetExternalFilesDir() for files that are private to your app, it's important that you use directory names provided by API constants like DIRECTORY_PICTURES. These directory names ensure that the files are treated properly by the system. For instance, files saved in DIRECTORY_RINGTONES are categorized by the system media scanner as ringtones instead of music.

譯:無論你是使用 getExternalStoragePublicDirectory()  方法爲了保存共享的文件還是使用 getExternalFilesDir() 方法爲了保存app 的私有文件,重要的是你要使用API 提供的像DIRECTORY_PICTURES 這樣的 目錄名字。這些目錄名字會確保那些文件被系統合適的處理。例如:保存在  DIRECTORY_RINGTONES  目錄下的文件會被系統的媒體掃描組織起來作爲鈴聲而不是音樂。

Query Free Space(查詢可用空間)

If you know ahead of time how much data you're saving, you can find out whether sufficient space is available without causing an IOException by calling getFreeSpace() or getTotalSpace(). These methods provide the current available space and the total space in the storage volume, respectively. This information is also useful to avoid filling the storage volume above a certain threshold.

譯:如果你能提前知道你存儲的數據有多大,你可以通過調用 getFreeSpace() 或者 getTotalSpace()方法發現是否有足夠的空間來保存文件,從而避免造成IO異常。那些方法可以分別得到存儲卷中當前可用的空間以及總空間。這些信息在避免有確定的閾值時寫數據到存儲卷非常有用。

However, the system does not guarantee that you can write as many bytes as are indicated by getFreeSpace(). If the number returned is a few MB more than the size of the data you want to save, or if the file system is less than 90% full, then it's probably safe to proceed. Otherwise, you probably shouldn't write to storage.

譯:然而,系統不會擔保你可以寫與方法 getFreeSpace() 指示的那樣多的字節數。如果返回的數字只有幾兆而不是你想要保存數據的大小,或者如果文件系統使用率少於90%,這時獲取可以安全的執行保存操作,否則你可能不應該執行寫操作。

Note: You aren't required to check the amount of available space before you save your file. You can instead try writing the file right away, then catch an IOException if one occurs. You may need to do this if you don't know exactly how much space you need. For example, if you change the file's encoding before you save it by converting a PNG image to JPEG, you won't know the file's size beforehand.

譯:注意:在保存你的文件時並沒有強制要求去檢查當前可用空間的總量。你可以嘗試寫文件,然後通過捕獲 IOException 如果發生了IO異常。在你不能精確知道你需要多大的 空間時你可能這樣做。例如:在你保存文件之前如果你把文件的編碼從PNG格式的圖片變爲JPEG格式的圖片,你處理之前無法知道文件的大小。

Delete a File(刪除一個文件)

You should always delete files that you no longer need. The most straightforward way to delete a file is to have the opened file reference call delete() on itself.

譯:你應該刪除一個文件當你不再需要它時。最簡單的刪除一個文件的方式是得到所打開文件的引用並且用它調用  delete() 。

myFile.delete();
If the file is saved on internal storage, you can also ask the Context to locate and delete a file by callingdeleteFile():

譯:如果文件保存在內置存儲中,你也可以通過Context去定位並刪除一個文件通過調用 deleteFile():

myContext.deleteFile(fileName);

Note: When the user uninstalls your app, the Android system deletes the following:(譯:當用戶卸載你的app時,Android系統會刪除以下文件:)

  • All files you saved on internal storage(譯:你保存在內存存儲中的所有文件)
  • All files you saved on external storage using getExternalFilesDir().(譯:通過使用 getExternalFilesDir()方法保存在外置存儲中的多有文件

However, you should manually delete all cached files created with getCacheDir() on a regular basis and also regularly delete other files you no longer need.(譯:然而,你應該手動刪除所有通過 getCacheDir() 方法得到目錄下的緩存文件以及那些不再需要的文件。)


翻譯自該原文:http://developer.android.com/training/basics/data-storage/files.html#InternalVsExternalStorage









發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章