打開手機設置,選擇應用管理,選擇任意一個App,然后你會看到兩個按鈕,一個是清除緩存,另一個是清除數據,那么當我們點擊清除緩存的時候清除的是哪里的數據?當我們點擊清除數據的時候又是清除的哪里的數據?讀完本文相信你會有答案。
在android開發中我們常常聽到這樣幾個概念,內存,內部存儲,外部存儲,很多人常常將這三個東西搞混,那么我們今天就先來詳細說說這三個東西是怎么回事?
內存,我們在英文中稱作memory,內部存儲,我們稱為InternalStorage,外部存儲我們稱為ExternalStorage,這在英文中本不會產生歧義,但是當我們翻譯為中文之后,前兩個都簡稱為內存,於是,混了。
那么究竟什么是內部存儲什么是外部存儲呢?
首先我們打開DDMS,有一個File Explorer,如下:
這里有三個文件夾需要我們重視,一個是data,一個是mnt,一個是storage,我們下面就詳細說說這三個文件夾。
1.內部存儲
data文件夾就是我們常說的內部存儲,當我們打開data文件夾之后(沒有root的手機不能打開該文件夾),里邊有兩個文件夾值得我們關注,如下:
一個文件夾是app文件夾,還有一個文件夾就是data文件夾,app文件夾里存放着我們所有安裝的app的apk文件,其實,當我們調試一個app的時候,可以看到控制台輸出的內容,有一項是uploading .....就是上傳我們的apk到這個文件夾,上傳成功之后才開始安裝。另一個重要的文件夾就是data文件夾了,這個文件夾里邊都是一些包名,打開這些包名之后我們會看到這樣的一些文件:
1.data/data/包名/shared_prefs
2.data/data/包名/databases
3.data/data/包名/files
4.data/data/包名/cache
如果打開過data文件,應該都知道這些文件夾是干什么用的,我們在使用sharedPreferenced的時候,將數據持久化存儲於本地,其實就是存在這個文件中的xml文件里,我們App里邊的數據庫文件就存儲於databases文件夾中,還有我們的普通數據存儲在files中,緩存文件存儲在cache文件夾中,存儲在這里的文件我們都稱之為內部存儲。
2.外部存儲
外部存儲才是我們平時操作最多的,外部存儲一般就是我們上面看到的storage文件夾,當然也有可能是mnt文件夾,這個不同廠家有可能不一樣。
一般來說,在storage文件夾中有一個sdcard文件夾,這個文件夾中的文件又分為兩類,一類是公有目錄,還有一類是私有目錄,其中的公有目錄有九大類,比如DCIM、DOWNLOAD等這種系統為我們創建的文件夾,私有目錄就是Android這個文件夾,這個文件夾打開之后里邊有一個data文件夾,打開這個data文件夾,里邊有許多包名組成的文件夾。
說到這里,我想大家應該已經可以分清楚什么是內部存儲什么是外部存儲了吧?好,分清楚之后我們就要看看怎么來操作內部存儲和外部存儲了。
3.操作存儲空間
首先,經過上面的分析,大家已經明白了,什么是內部存儲,什么是外部存儲,以及這兩種存儲方式分別存儲在什么位置,一般來說,我們不會自己去操作內部存儲空間,沒有root權限的話,我們也沒法操作內部存儲空間,事實上內部存儲主要是由系統來維護的。不過在代碼中我們是可以訪問到這個文件夾的。由於內部存儲空間有限,在開發中我們一般都是操作外部存儲空間,Google官方建議我們App的數據應該存儲在外部存儲的私有目錄中該App的包名下,這樣當用戶卸載掉App之后,相關的數據會一並刪除,如果你直接在/storage/sdcard目錄下創建了一個應用的文件夾,那么當你刪除應用的時候,這個文件夾就不會被刪除。
經過以上的介紹,我們可以總結出下面一個表格:
一目了然,什么是內部存儲,什么是外部存儲。
如果按照路徑的特征,我們又可以將文件存儲的路徑分為兩大類,一類是路徑中含有包名的,一類是路徑中不含有包名的,含有包名的路徑,因為和某個App有關,所以對這些文件夾的訪問都是調用Context里邊的方法,而不含有包名的路徑,和某一個App無關,我們可以通過Environment中的方法來訪問。如下圖:
大家看到,有包名的路徑我們都是調用Context中的方法來獲得,沒有包名的路徑,我們直接調用Environment中的方法獲得,那么其中有兩個方法需要傳入一個String類型的參數,這個參數我們使用了Environment中的常量,參數的意思是我們要訪問這個路徑下的哪個文件夾,比如getExternalFilesDir方法,我們看看它的源碼:
1 /** 2 * 3 * @param type The type of files directory to return. May be null for 4 * the root of the files directory or one of 5 * the following Environment constants for a subdirectory: 6 * {@link android.os.Environment#DIRECTORY_MUSIC}, 7 * {@link android.os.Environment#DIRECTORY_PODCASTS}, 8 * {@link android.os.Environment#DIRECTORY_RINGTONES}, 9 * {@link android.os.Environment#DIRECTORY_ALARMS}, 10 * {@link android.os.Environment#DIRECTORY_NOTIFICATIONS}, 11 * {@link android.os.Environment#DIRECTORY_PICTURES}, or 12 * {@link android.os.Environment#DIRECTORY_MOVIES}. 13 * 14 * @return The path of the directory holding application files 15 * on external storage. Returns null if external storage is not currently 16 * mounted so it could not ensure the path exists; you will need to call 17 * this method again when it is available. 18 * 19 * @see #getFilesDir 20 * @see android.os.Environment#getExternalStoragePublicDirectory 21 */ 22 @Nullable 23 public abstract File getExternalFilesDir(@Nullable String type); 24 它的注釋非常多,我這里只列出其中一部分,我們看到,我們可以訪問files文件夾下的Music文件夾、Movies文件夾等等好幾種。 25 26 27 28 說到這里,我想大家對內部存儲、外部存儲該有了一個清晰的認識了吧。我們在開發中,不建議往內部存儲中寫太多的數據,畢竟空間有限。外部存儲在使用的時候最好能夠將文件存放在私有目錄下,這樣有利於系統維護,也避免用戶的反感。 29 30 現在我們再來看看我們一開始提出的問題,當我們點擊清除數據的時候清除的是哪里的數據呢?毫無疑問,當然是內部存儲目錄中相應的files和cache文件夾中的文件和外部存儲中相應的files和cache文件夾中的文件,至於這些文件夾的路徑我想你應該已經明白了。 31 32 好了,最后再送給大家一個文件操作工具類: 33 34 35 36 public class SDCardHelper { 37 38 // 判斷SD卡是否被掛載 39 public static boolean isSDCardMounted() { 40 // return Environment.getExternalStorageState().equals("mounted"); 41 return Environment.getExternalStorageState().equals( 42 Environment.MEDIA_MOUNTED); 43 } 44 45 // 獲取SD卡的根目錄 46 public static String getSDCardBaseDir() { 47 if (isSDCardMounted()) { 48 return Environment.getExternalStorageDirectory().getAbsolutePath(); 49 } 50 return null; 51 } 52 53 // 獲取SD卡的完整空間大小,返回MB 54 public static long getSDCardSize() { 55 if (isSDCardMounted()) { 56 StatFs fs = new StatFs(getSDCardBaseDir()); 57 long count = fs.getBlockCountLong(); 58 long size = fs.getBlockSizeLong(); 59 return count * size / 1024 / 1024; 60 } 61 return 0; 62 } 63 64 // 獲取SD卡的剩余空間大小 65 public static long getSDCardFreeSize() { 66 if (isSDCardMounted()) { 67 StatFs fs = new StatFs(getSDCardBaseDir()); 68 long count = fs.getFreeBlocksLong(); 69 long size = fs.getBlockSizeLong(); 70 return count * size / 1024 / 1024; 71 } 72 return 0; 73 } 74 75 // 獲取SD卡的可用空間大小 76 public static long getSDCardAvailableSize() { 77 if (isSDCardMounted()) { 78 StatFs fs = new StatFs(getSDCardBaseDir()); 79 long count = fs.getAvailableBlocksLong(); 80 long size = fs.getBlockSizeLong(); 81 return count * size / 1024 / 1024; 82 } 83 return 0; 84 } 85 86 // 往SD卡的公有目錄下保存文件 87 public static boolean saveFileToSDCardPublicDir(byte[] data, String type, 88 String fileName) { 89 BufferedOutputStream bos = null; 90 if (isSDCardMounted()) { 91 File file = Environment.getExternalStoragePublicDirectory(type); 92 try { 93 bos = new BufferedOutputStream(new FileOutputStream(new File( 94 file, fileName))); 95 bos.write(data); 96 bos.flush(); 97 return true; 98 } catch (Exception e) { 99 e.printStackTrace(); 100 } finally { 101 try { 102 bos.close(); 103 } catch (IOException e) { 104 // TODO Auto-generated catch block 105 e.printStackTrace(); 106 } 107 } 108 } 109 return false; 110 } 111 112 // 往SD卡的自定義目錄下保存文件 113 public static boolean saveFileToSDCardCustomDir(byte[] data, String dir, 114 String fileName) { 115 BufferedOutputStream bos = null; 116 if (isSDCardMounted()) { 117 File file = new File(getSDCardBaseDir() + File.separator + dir); 118 if (!file.exists()) { 119 file.mkdirs();// 遞歸創建自定義目錄 120 } 121 try { 122 bos = new BufferedOutputStream(new FileOutputStream(new File( 123 file, fileName))); 124 bos.write(data); 125 bos.flush(); 126 return true; 127 } catch (Exception e) { 128 e.printStackTrace(); 129 } finally { 130 try { 131 bos.close(); 132 } catch (IOException e) { 133 // TODO Auto-generated catch block 134 e.printStackTrace(); 135 } 136 } 137 } 138 return false; 139 } 140 141 // 往SD卡的私有Files目錄下保存文件 142 public static boolean saveFileToSDCardPrivateFilesDir(byte[] data, 143 String type, String fileName, Context context) { 144 BufferedOutputStream bos = null; 145 if (isSDCardMounted()) { 146 File file = context.getExternalFilesDir(type); 147 try { 148 bos = new BufferedOutputStream(new FileOutputStream(new File( 149 file, fileName))); 150 bos.write(data); 151 bos.flush(); 152 return true; 153 } catch (Exception e) { 154 e.printStackTrace(); 155 } finally { 156 try { 157 bos.close(); 158 } catch (IOException e) { 159 // TODO Auto-generated catch block 160 e.printStackTrace(); 161 } 162 } 163 } 164 return false; 165 } 166 167 // 往SD卡的私有Cache目錄下保存文件 168 public static boolean saveFileToSDCardPrivateCacheDir(byte[] data, 169 String fileName, Context context) { 170 BufferedOutputStream bos = null; 171 if (isSDCardMounted()) { 172 File file = context.getExternalCacheDir(); 173 try { 174 bos = new BufferedOutputStream(new FileOutputStream(new File( 175 file, fileName))); 176 bos.write(data); 177 bos.flush(); 178 return true; 179 } catch (Exception e) { 180 e.printStackTrace(); 181 } finally { 182 try { 183 bos.close(); 184 } catch (IOException e) { 185 // TODO Auto-generated catch block 186 e.printStackTrace(); 187 } 188 } 189 } 190 return false; 191 } 192 193 // 保存bitmap圖片到SDCard的私有Cache目錄 194 public static boolean saveBitmapToSDCardPrivateCacheDir(Bitmap bitmap, 195 String fileName, Context context) { 196 if (isSDCardMounted()) { 197 BufferedOutputStream bos = null; 198 // 獲取私有的Cache緩存目錄 199 File file = context.getExternalCacheDir(); 200 201 try { 202 bos = new BufferedOutputStream(new FileOutputStream(new File( 203 file, fileName))); 204 if (fileName != null 205 && (fileName.contains(".png") || fileName 206 .contains(".PNG"))) { 207 bitmap.compress(Bitmap.CompressFormat.PNG, 100, bos); 208 } else { 209 bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bos); 210 } 211 bos.flush(); 212 } catch (Exception e) { 213 e.printStackTrace(); 214 } finally { 215 if (bos != null) { 216 try { 217 bos.close(); 218 } catch (IOException e) { 219 e.printStackTrace(); 220 } 221 } 222 } 223 return true; 224 } else { 225 return false; 226 } 227 } 228 229 // 從SD卡獲取文件 230 public static byte[] loadFileFromSDCard(String fileDir) { 231 BufferedInputStream bis = null; 232 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 233 234 try { 235 bis = new BufferedInputStream( 236 new FileInputStream(new File(fileDir))); 237 byte[] buffer = new byte[8 * 1024]; 238 int c = 0; 239 while ((c = bis.read(buffer)) != -1) { 240 baos.write(buffer, 0, c); 241 baos.flush(); 242 } 243 return baos.toByteArray(); 244 } catch (Exception e) { 245 e.printStackTrace(); 246 } finally { 247 try { 248 baos.close(); 249 bis.close(); 250 } catch (IOException e) { 251 e.printStackTrace(); 252 } 253 } 254 return null; 255 } 256 257 // 從SDCard中尋找指定目錄下的文件,返回Bitmap 258 public Bitmap loadBitmapFromSDCard(String filePath) { 259 byte[] data = loadFileFromSDCard(filePath); 260 if (data != null) { 261 Bitmap bm = BitmapFactory.decodeByteArray(data, 0, data.length); 262 if (bm != null) { 263 return bm; 264 } 265 } 266 return null; 267 } 268 269 // 獲取SD卡公有目錄的路徑 270 public static String getSDCardPublicDir(String type) { 271 return Environment.getExternalStoragePublicDirectory(type).toString(); 272 } 273 274 // 獲取SD卡私有Cache目錄的路徑 275 public static String getSDCardPrivateCacheDir(Context context) { 276 return context.getExternalCacheDir().getAbsolutePath(); 277 } 278 279 // 獲取SD卡私有Files目錄的路徑 280 public static String getSDCardPrivateFilesDir(Context context, String type) { 281 return context.getExternalFilesDir(type).getAbsolutePath(); 282 } 283 284 public static boolean isFileExist(String filePath) { 285 File file = new File(filePath); 286 return file.isFile(); 287 } 288 289 // 從sdcard中刪除文件 290 public static boolean removeFileFromSDCard(String filePath) { 291 File file = new File(filePath); 292 if (file.exists()) { 293 try { 294 file.delete(); 295 return true; 296 } catch (Exception e) { 297 return false; 298 } 299 } else { 300 return false; 301 } 302 } 303 }
本文相關筆記和源碼下載http://download.csdn.net/detail/u012702547/9348985