標籤:存在 其他 理解 移動硬碟 fuse can 文本 tput 環境
摘要 其實安卓檔案的操作和Java在pc環境下的操作並無二致,之所以需要單獨講解是因為安卓系統提供了不同於pc的訪問檔案系統根路徑的api,同時對一個應用的私人檔案做了統一的管理。根據我的經驗,初學者在這部分感到很容易混淆內部儲存和外部儲存兩個概念。 相對
其實安卓檔案的操作和java在pc環境下的操作並無二致,之所以需要單獨講解是因為安卓系統提供了不同於pc的訪問檔案系統根路徑的api,同時對一個應用的私人檔案做了統一的管理。根據我的經驗,初學者在這部分感到很容易混淆內部儲存和外部儲存兩個概念。
相對路徑和絕對路徑
在java中,關於相對路徑和絕對路徑是這樣解釋的,如果你很熟悉這部分以下灰色文字可以跳過:
絕對路徑是指書寫檔案的完整路徑,例如d:\java\Hello.java,該路徑中包含檔案的完整路徑d:\java以及檔案的全名Hello.java。使用該路徑可以唯一的找到一個檔案,不會產生歧義。但是使用絕對路徑在表示檔案時,受到的限制很大,且不能在不同的作業系統下運行,因為不同作業系統下絕對路徑的表達形式存在不同。
相對路徑是指書寫檔案的部分路徑,例如\test\Hello.java,該路徑中只包含檔案的部分路徑\test和檔案的全名Hello.java,部分路徑是指當前路徑下的子路徑,例如當前程式在d:\abc下運行,則該檔案的完整路徑就是d:\abc\test。使用這種形式,可以更加通用的代表檔案的位置,使得檔案路徑產生一定的靈活性。
在Eclipse項目中運行程式時,當前路徑是項目的根目錄,例如工作空間儲存在d:\javaproject,當前項目名稱是Test,則當前路徑是:d:\javaproject\Test。在控制台下面運行程式時,當前路徑是class檔案所在的目錄,如果class檔案包含包名,則以該class檔案最頂層的包名作為當前路徑。
這是java在多數作業系統中這樣操作,很顯然是要我們儘可能的使用相對路徑,但是在安卓中,其實多數情況下我們都是使用的絕對路徑。為什麼呢?注意上面說到相對路徑是以當前項目所在路徑為當前路徑,但在安卓中我們是不可能在項目所在路徑目錄下做任何操作的,因為普通java中我們的項目建立於伺服器(pc也算是伺服器),運行於伺服器,我們當然能在伺服器操作自己的檔案目錄。但是安卓開發中,我們的項目一般是建立於自己工作的電腦,而運行於手機,既然apk已經運行於手機了,那項目就已經部署到手機上了,應該以apk在手機上的位置來確定相對路徑,但我們好像們沒有辦法操作這個路徑的,因為apk是在system目錄下,就算可以操作,在這個目錄下存取檔案也是沒有意義的,比如我寫一個相簿程式,圖片肯定是放在外部儲存中,而如果我要儲存一個應用的一些設定資料,我是放在內部儲存的data目錄下,因此其實在安卓檔案管理中,我們都是在操作絕對路徑。
File類
操作一個檔案(讀寫,建立檔案或者目錄)是通過File類來完成的,這個操作和java中完全一致。
外部儲存external storage和內部儲存internal storage
1.內部儲存:
注意內部儲存不是記憶體。內部儲存位於系統中很特殊的一個位置,如果你想將檔案儲存體於內部儲存中,那麼檔案預設只能被你的應用訪問到,且一個應用所建立的所有檔案都在和應用程式套件名相同的目錄下。也就是說應用建立於內部儲存的檔案,與這個應用是關聯起來的。當一個應用卸載之後,內部儲存中的這些檔案也被刪除。從技術上來講如果你在建立內部隱藏檔的時候將檔案屬性設定成可讀,其他app能夠訪問自己應用的資料,前提是他知道你這個應用的包名,如果一個檔案的屬性是私人(private),那麼即使知道包名其他應用也無法訪問。 內部儲存空間十分有限,因而顯得可貴,另外,它也是系統本身和系統應用程式主要的資料存放區所在地,一旦內部儲存空間耗盡,手機也就無法使用了。所以對於內部儲存空間,我們要盡量避免使用。Shared Preferences和SQLite資料庫都是儲存在內部儲存空間上的。內部儲存一般用Context來擷取和操作。
getFilesDir()擷取你app的內部儲存空間,相當於你的應用在內部儲存上的根目錄。
如果是要建立一個檔案,如下
1 |
File file = newFile(context.getFilesDir(), filename); |
安卓還為我們提供了一個簡便方法 openFileOutput()
來讀寫應用在內部儲存空間上的檔案,下面是一個向檔案中寫入文本的例子:
12345678910 |
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(); } |
內部儲存的其他一些操作:
A.列出所有的已建立的檔案,這個可能不容易想到,Context
居然有這樣的方法。
1234 |
String[] files = Context.fileList(); for (String file : files) { Log.e(TAG, "file is " + file); } |
B.刪除檔案,能建立就要能夠刪除,當然也會提供了刪除檔案的介面,它也非常簡單,只需要提供檔案名稱
12345 |
if (Context.deleteFile(filename)) { Log.e(TAG, "delete file " + filename + " sucessfully“); } else { Log.e(TAG, " failed to deletefile " + filename); } |
C.建立一個目錄,需要傳入目錄名稱,它返回 一個檔案對象用到操作路徑
12 |
File workDir = Context.getDir(dirName, Context.MODE_PRIVATE); Log.e(TAG, "workdir " + workDir.getAbsolutePath(); |
總結一下檔案相關操作,可以得出以下三個特點:
1. 檔案操作只需要向函數提供檔案名稱,所以程式自己只需要維護檔案名稱即可;
2. 不用自己去建立檔案對象和輸入、輸出資料流,提供檔案名稱就可以返回File對象或輸入輸出資料流
3. 對於路徑操作返回的都是檔案對象。
2.外部儲存:
最容易混淆的是外部儲存,如果說pc上也要區分出外部儲存和內部儲存的話,那麼內建的硬碟算是內部儲存,隨身碟或者移動硬碟算是外部儲存,因此我們很容易帶著這樣的理解去看待安卓手機,認為機身固有儲存是內部儲存,而擴充的T卡是外部儲存。比如我們任務16GB版本的Nexus 4有16G的內部儲存,普通消費者可以這樣理解,但是安卓的編程中不能,這16GB仍然是外部儲存。
所有的安卓裝置都有外部儲存和內部儲存,這兩個名稱來源於安卓的早期裝置,那個時候的裝置內部儲存確實是固定的,而外部儲存確實是可以像隨身碟一樣移動的。但是在後來的裝置中,很多中高端機器都將自己的機身儲存擴充到了8G以上,他們將儲存在概念上分成了"內部internal" 和"外部external" 兩部分,但其實都在手機內部。所以不管安卓手機是否有可移動的sdcard,他們總是有外部儲存和內部儲存。最關鍵的是,我們都是通過相同的api來訪問可移動的sdcard或者手機內建的儲存(外部儲存)。
外部儲存雖然概念上有點複雜,但也很好區分,你把手機串連電腦,能被電腦識別的部分就一定是外部儲存。
關於外部儲存,我覺得api中在介紹Environment.getExternalStorageDirectory()方法的時候說得很清楚:
don‘t be confused by the word "external" here. This directory can better be thought as media/shared storage. It is a filesystem that can hold a relatively large amount of data and that is shared across all applications (does not enforce permissions). Traditionally this is an SD card, but it may also be implemented as built-in storage in a device that is distinct from the protected internal storage and can be mounted as a filesystem on a computer.
看不懂沒關係,其實跟我說的意思差不多,只是覺得說得比較形象,不知道是我的表述問題,還是英文在邏輯解釋方面比漢語強,因為白話文其實是被閹割的漢語。
外部儲存中的檔案是可以被使用者或者其他應用程式修改的,有兩種類型的檔案(或者目錄):
1.公用檔案Public files:檔案是可以被自由訪問,且檔案的資料對其他應用或者使用者來說都是由意義的,當應用被卸載之後,其卸載前建立的檔案仍然保留。比如camera應用,產生的照片大家都能訪問,而且camera不在了,照片仍然在。
如果你想在外儲存上放公用檔案你可以使用getExternalStoragePublicDirectory()
123456789 |
public File getAlbumStorageDir(String albumName) { // Get the directory for the user‘s public pictures directory. File file = newFile(Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_PICTURES), albumName); if (!file.mkdirs()) { Log.e(LOG_TAG, "Directory not created" ); } returnfile; } |
在上面的代碼中我們建立獲得了存放picture的目錄,並且新建立一個albumName檔案。
如果你的api 版本低於8,那麼不能使用getExternalStoragePublicDirectory(),而是使用Environment
.getExternalStorageDirectory(),他不帶參數,也就不能自己建立一個目錄,只是返回外部儲存的根路徑。
2.私人檔案Private files:其實由於是外部儲存的原因即是是這種類型的檔案也能被其他程式訪問,只不過一個應用私人的檔案對其他應用其實是沒有訪問價值的(惡意程式除外)。外部儲存上,應用私人檔案的價值在於卸載之後,這些檔案也會被刪除。類似於內部儲存。
建立應用私人檔案的方法是Context.getExternalFilesDir(),如下:
123456789 |
public File getAlbumStorageDir(Context context, String albumName) { // Get the directory for the app‘s private pictures directory. File file = newFile(context.getExternalFilesDir( Environment.DIRECTORY_PICTURES), albumName); if (!file.mkdirs()) { Log.e(LOG_TAG, "Directory not created" ); } returnfile; } |
上面的代碼建立了一個picture目錄,並在這個目錄下建立了一個名為albumName
的檔案,
Environment.DIRECTORY_PICTURES
其實就是字串picture。
所有應用程式的外部儲存的私人檔案都放在根目錄的Android/data/下,目錄形式為/Android/data/<package_name>/
如果你的api 版本低於8,那麼不能使用getExternalFilesDir()
,而是使用Environment
.getExternalStorageDirectory()獲得根路徑之後,自己再想辦法操作/Android/data/<package_name>/下的檔案。
也就是說api 8以下的版本在操作檔案的時候沒有專門為私人檔案和公用檔案的操作提供api支援。你只能先擷取根目錄,然後自行想辦法。
在使用外部儲存之前,你必須要先檢查外部儲存的目前狀態,以判斷是否可用。
123456789101112131415 |
boolean mExternalStorageAvailable = false ; boolean mExternalStorageWriteable = false ; String state = Environment.getExternalStorageState(); if (Environment.MEDIA_MOUNTED.equals(state)) { // We can read and write the media mExternalStorageAvailable = mExternalStorageWriteable = true ; } elseif(Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) { // We can only read the media mExternalStorageAvailable = true ; mExternalStorageWriteable = false ; } else { // Something else is wrong. It may be one of many other states, but all we need // to know is we can neither read nor write mExternalStorageAvailable = mExternalStorageWriteable = false ; } |
最後為了弄清楚getFilesDir,getExternalFilesDir,getExternalStorageDirectory,getExternalStoragePublicDirectory等android檔案操作方法,我將這些方法的執行結果列印出來,看看到底路徑是啥樣,在activity中執行以下代碼:
123456 |
Log.i( "codecraeer" , "getFilesDir = " + getFilesDir()); Log.i( "codecraeer" , "getExternalFilesDir = " + getExternalFilesDir( "exter_test" ).getAbsolutePath()); Log.i( "codecraeer" , "getDownloadCacheDirectory = " + Environment.getDownloadCacheDirectory().getAbsolutePath()); Log.i( "codecraeer" , "getDataDirectory = " + Environment.getDataDirectory().getAbsolutePath()); Log.i( "codecraeer" , "getExternalStorageDirectory = " + Environment.getExternalStorageDirectory().getAbsolutePath()); Log.i( "codecraeer" , "getExternalStoragePublicDirectory = " + Environment.getExternalStoragePublicDirectory( "pub_test" )); |
在log中看到如下結果:
從log中我們可以看到外部儲存根目錄在我手機(nexus 3)上是/storage/emulated/0,奇怪的是在有些手機上同樣的代碼卻是下面的情況:
部儲存根目錄為/mnt/sdcard.
在網上搜了下好像是說三星手機就是這樣。
參考文章:http://developer.android.com/training/basics/data-storage/files.html#InternalVsExternalStorage
http://developer.android.com/guide/topics/data/data-storage.html
from:http://blog.csdn.net/androidwifi/article/details/17725989/
【轉】 android中的檔案操作詳解以及內部儲存和外部儲存