編輯:關於Android編程
所有的Android設備均有兩個文件存儲區域:”internal” 與 “external” 。 這兩個名稱來自於早先的Android系統,當時大多設備都內置了不可變的內存(internal storage)及一個類似於SD card(external storage)這樣的可卸載的存儲部件。之後有一些設備將”internal” 與 “external” 都做成了不可卸載的內置存儲,雖然如此,但是這一整塊還是從邏輯上有被劃分為”internal”與”external”的。只是現在不再以是否可卸載進行區分了。
Internal storage
- 總是可用的
- 這裡的文件默認只能被我們的app所訪問。
- 當用戶卸載app的時候,系統會把internal內該app相關的文件都清除干淨。
- Internal是我們在想確保不被用戶與其他app所訪問的最佳存儲區域。
External storage
- 並不總是可用的,因為用戶有時會通過USB存儲模式掛載外部存儲器,當取下掛載的這部分後,就無法對其進行訪問了。
- 是大家都可以訪問的,因此保存在這裡的文件可能被其他程序訪問。
- 當用戶卸載我們的app時,系統僅僅會刪除external根目錄(getExternalFilesDir())下的相關文件。
- External是在不需要嚴格的訪問權限並且希望這些文件能夠被其他app所共享或者是允許用戶通過電腦訪問時的最佳存儲區域。
Note:盡管app是默認被安裝到internal storage的,我們還是可以通過在程序的manifest文件中聲明android:installLocation 屬性來指定程序安裝到external storage。當某個程序的安裝文件很大且用戶的external storage空間大於internal storage時,用戶會傾向於將該程序安裝到external storage。
對於android的存儲,其實就是使用java對文件進行存儲或讀取
當保存文件到internal storage時,可以通過執行下面兩個方法之一來獲取合適的目錄作為 FILE 的對象:
- getFilesDir() : 返回一個File,代表了我們app的internal目錄。
- getCacheDir() : 返回一個File,代表了我們app的internal緩存目錄。請確保這個目錄下的文件能夠在一旦不再需要的時候馬上被刪除,並對其大小進行合理限制,例如1MB 。系統的內部存儲空間不夠時,會自行選擇刪除緩存文件。
可以使用File() 構造器在那些目錄下創建一個新的文件,然後進行存儲;同樣,也可以執行openFileOutput() 獲取一個 FileOutputStream 用於寫文件到internal目錄。
向內部存儲中寫內容:
//文件默認會存儲到/data/data/package/files中; private void write2FileMethod1(String data, int mode) { FileOutputStream outputStream; try { outputStream = openFileOutput(FILE_NAME, mode); outputStream.write(data.getBytes("UTF-8")); outputStream.close(); } catch (Exception e) { e.printStackTrace(); } } private void write2FileMethod2(String data) { FileOutputStream outputStream; try { outputStream = new FileOutputStream(getFile()); outputStream.write(data.getBytes()); outputStream.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } private File getFile() { return new File(this.getFilesDir(), FILE_NAME); }
從內部存儲中讀取內容
private void readFromFile() { FileInputStream in; ByteArrayOutputStream bout; byte[]buf = new byte[1024]; bout = new ByteArrayOutputStream(); int length; try { in = this.openFileInput(FILE_NAME); while((length = in.read(buf))!=-1){ bout.write(buf,0,length); } byte[] content = bout.toByteArray(); mDataTv.setText(new String(content,"UTF-8")); in.close(); bout.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
為確保外置存儲可用,我們一般會進行判斷:
/* 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; }
有的手機會有外置存儲和內置存儲的區分,即手機自帶的外置存儲和擴展的外置存儲的分別,這可以通過某些方法來區分。
盡管external storage對於用戶與其他app是可修改的,我們可能會保存下面兩種類型的文件。
- Public files :這些文件對與用戶與其他app來說是public的,當用戶卸載我們的app時,這些文件應該保留。例如,那些被我們的app拍攝的圖片或者下載的文件。
- Private files: 這些文件完全被我們的app所私有,它們應該在app被卸載時刪除。盡管由於存儲在external storage,那些文件從技術上而言可以被用戶與其他app所訪問,但實際上那些文件對於其他app沒有任何意義。因此,當用戶卸載我們的app時,系統會刪除其下的private目錄。例如,那些被我們的app下載的緩存文件。
想要將文件以public形式保存在external storage中,請使用getExternalStoragePublicDirectory()方法來獲取一個 File 對象,該對象表示存儲在external storage的目錄。這個方法會需要帶有一個特定的參數來指定這些public的文件類型,以便於與其他public文件進行分類。參數類型包括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; }
想要將文件以private形式保存在external storage中,可以通過執行getExternalFilesDir() 來獲取相應的目錄,並且傳遞一個指示文件類型的參數。每一個以這種方式創建的目錄都會被添加到external storage封裝我們app目錄下的參數文件夾下(如下則是albumName)。這下面的文件會在用戶卸載我們的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; }
如果剛開始的時候,沒有預定義的子目錄存放我們的文件,可以在 getExternalFilesDir()方法中傳遞null. 它會返回app在external storage下的private的根目錄。
getExternalFilesDir() 方法會創建的目錄會在app被卸載時被系統刪除。如果我們的文件想在app被刪除時仍然保留,請使用getExternalStoragePublicDirectory()
/** * 如果想更換存儲路徑請更換 * @param childFile * @return */ private File getFile(String childFile) { return new File(getExternalFilesDir(childFile), FILE_NAME); } private void write2File(String fileName, String data, boolean isAppend) { try { FileOutputStream outputStream = new FileOutputStream(getFile(fileName), isAppend); outputStream.write(data.getBytes()); outputStream.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } private void readFromFile(String fileName) { if(!isExternalStorageReadAble()) { showToast("外置存儲不可讀"); return; } ByteArrayOutputStream outputStream; byte[] buf = new byte[1024]; outputStream = new ByteArrayOutputStream(); int length; try { FileInputStream inputStream = new FileInputStream(getFile(fileName)); while ((length = inputStream.read(buf)) != -1) { outputStream.write(buf, 0, length); } byte[] content = outputStream.toByteArray(); mDataTv.setText(new String(content, "utf-8")); outputStream.close(); inputStream.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
默認情況下,上述寫內容的時候是MODE_PRIVATE的,如果想要使用APPEND模式使用下方幾種方式:
/** * 追加文件:使用FileOutputStream,在構造FileOutputStream時,把第二個參數設為true */ public static void method1(String file, String conent) { BufferedWriter out = null; try { out = new BufferedWriter(new OutputStreamWriter( new FileOutputStream(file, true))); out.write(conent); } catch (Exception e) { e.printStackTrace(); } finally { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } /** * 追加文件:使用FileWriter * * @param fileName * @param content */ public static void method2(String fileName, String content) { try { // 打開一個寫文件器,構造函數中的第二個參數true表示以追加形式寫文件 FileWriter writer = new FileWriter(fileName, true); writer.write(content); writer.close(); } catch (IOException e) { e.printStackTrace(); } } /** * 追加文件:使用RandomAccessFile * * @param fileName * 文件名 * @param content * 追加的內容 */ public static void method3(String fileName, String content) { try { // 打開一個隨機訪問文件流,按讀寫方式 RandomAccessFile randomFile = new RandomAccessFile(fileName, "rw"); // 文件長度,字節數 long fileLength = randomFile.length(); // 將寫文件指針移到文件尾。 randomFile.seek(fileLength); randomFile.writeBytes(content); randomFile.close(); } catch (IOException e) { e.printStackTrace(); } }
可以看出,其實讀或寫內存或外存的內容不過是將文件的路徑改變了而已。
在不需要使用某些文件的時候應刪除它。刪除文件最直接的方法是直接執行文件的delete()方法。
myFile.delete();
保存在內部存儲中的文件,也可以使用deleteFleece(fileName)來刪除:
myContext.deleteFile(fileName);
Note: 當用戶卸載我們的app時,android系統會刪除以下文件:
- 所有保存到internal storage的文件。
- 所有使用getExternalFilesDir()方式保存在external storage的文件。
然而,通常來說,我們應該手動刪除所有通過 getCacheDir() 方式創建的緩存文件,以及那些不會再用到的文件。
具體demo在項目中storage包名下
實現功能:退出應用時,保存歌曲位置(也就是當前是第幾首歌曲)退出應用時,保存播放模式(也就是用戶設置的是順序播放/隨機播放/單曲循環)進入應用時,讀取歌曲位置進入應用時,
前言:也許是低門檻原因,最初接觸Android的人寫了很多書、博文,創造了一個邪論:Activity就是弄界面的,Service就是弄後台的,進而將“播放音樂
ScrollView和ListView這兩個控件想必大家都不會陌生,但是這兩者嵌套使用的時候就會出現麻煩。比如,我們如果想在ListView下面添加其他的布局或者控件,然
目錄:1.Menu概述2.Menu的一些常用屬性3.Menu分類3.1 選項菜單(Option Menu)3.2 上下文菜單(Context Menu)3.3 彈出菜單(