访问SD卡 所需权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
App专属文件 vs App独立文件
app专属文件就是那些只有该app才可以使用的文件,例如专属格式的电子书,app独立文件就是那些不依赖于某特定app的文件,例如照片。
App独立文件
这类文件当我们删除应用之后,还应该保留在手机上的,例如拍照的照片,不应该随着删除应用而被删除掉。对于这类文件,android给我们提供了特定的目录,这些目录都是以DIRECTORY开头的,例如:DIRECTORY_MUSIC , DIRECTORY_PICTURES.
访问这些文件夹有两种方式:
Environment.getExternalStorageDirectory()是获得外部存储的第一层的对象
第一种:File sdCard = Environment.getExternalStorageDirectory();
这个sdCard的路径为mnt/sdcard/ 即为SD卡根路径,我们可以指定访问的文件夹名
File sdCard = Environment.getExternalStorageDirectory();
File directory_pictures = new File(sdCard, "Pictures");
Log.i(TAG,"directory_pictures="+directory_pictures);
得到的路径如下:
第二种:Environment.getExternalStoragePublicDirectory(String type)
如果您需要往sdcard中保存特定类型的内容,可以考虑使用Environment.getExternalStoragePublicDirectory(String type)函数,该函数可以返回特定类型的目录,目前支持如下类型:
DIRECTORY_ALARMS //警报的铃声
DIRECTORY_DCIM //相机拍摄的图片和视频保存的位置
DIRECTORY_DOWNLOADS //下载文件保存的位置
DIRECTORY_MOVIES //电影保存的位置, 比如 通过google play下载的电影
DIRECTORY_MUSIC //音乐保存的位置
DIRECTORY_NOTIFICATIONS //通知音保存的位置
DIRECTORY_PICTURES //下载的图片保存的位置
DIRECTORY_PODCASTS //用于保存podcast(博客)的音频文件
DIRECTORY_RINGTONES //保存铃声的位置
File directory_pictures = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); Log.e(TAG, "directory_pictures="+directory_pictures);
得到的路径如下:
第二种方法是一个更加方便的访问Android给我们提供好的一些公共目录的方法,第一种方式更加灵活,可以自己指定目录。
===================================================================
Android系统提供了Environment.getExternalStorageDirectory()接口获得存储设备的路径,但是这个接口往往给出的结果并不是我们想要的,在某些设备上它返回的是手机内部存储,某些设备上返回的手机外部存储。还有就是某些android设备支持扩展多个sdcard,这个时候想要获得所有存储器的挂载路径,这个接口是没有办法办到的。
那么,Android系统的文件管理器是如何把所有挂载的存储设备加载出来的呢?通过查看文件管理器的源码发现是在MountPointManager类中处理的,通过调用StorageManager类的getVolumeList()方法获取的。
[java] view plain copy
1. /**
2. * This method initializes MountPointManager.
3. *
4. * @param context Context to use
5. */
6. public void init(Context context) {
7. mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
8. final String defaultPath = getDefaultPath();
9. "init,defaultPath = " + defaultPath);
10. if (!TextUtils.isEmpty(defaultPath)) {
11. mRootPath = ROOT_PATH;
12. }
13. mMountPathList.clear();
14. // check media availability to init mMountPathList
15. StorageVolume[] storageVolumeList = mStorageManager.getVolumeList();
16. if (storageVolumeList != null) {
17. for (StorageVolume volume : storageVolumeList) {
18. new MountPoint();
19. mountPoint.mDescription = volume.getDescription(context);
20. mountPoint.mPath = volume.getPath();
21. mountPoint.mIsMounted = isMounted(volume.getPath());
22. mountPoint.mIsExternal = volume.isRemovable();
23. mountPoint.mMaxFileSize = volume.getMaxFileSize();
24. "init,description :" + mountPoint.mDescription + ",path : "
25. ",isMounted : " + mountPoint.mIsMounted
26. ",isExternal : " + mountPoint.mIsExternal + ", mMaxFileSize: " + mountPoint.mMaxFileSize);
27. mMountPathList.add(mountPoint);
28. }
29. }
30. IconManager.getInstance().init(context, defaultPath + SEPARATOR);
31. }
系统提供了StorageManager类,它有一个方法叫getVolumeList(),这个方法的返回值是一个StorageVolume数组,StorageVolume类中封装了挂载路径,挂载状态,以及是否可以移除等信息。下面是这个方法的源码。
[java] view plain copy
1. /**
2. * Returns list of all mountable volumes.
3. * @hide
4. */
5. public StorageVolume[] getVolumeList() {
6. if (mMountService == null) return new StorageVolume[0];
7. try {
8. Parcelable[] list = mMountService.getVolumeList();
9. if (list == null) return new StorageVolume[0];
10. int length = list.length;
11. new StorageVolume[length];
12. for (int i = 0; i < length; i++) {
13. result[i] = (StorageVolume)list[i];
14. }
15. return result;
16. catch (RemoteException e) {
17. "Failed to get volume list", e);
18. return null;
19. }
20. }
getVolumeList()方法是隐藏的,不能在应用代码中直接调用,所以我们只能通过反射来调用这个方法了。
通过反射机制获取Android设备的所有存储设备
[java] view plain copy
1. public class StorageInfo {
2. public String path;
3. public String state;
4. public boolean isRemoveable;
5. public StorageInfo(String path) {
6. this.path = path;
7. }
8. public boolean isMounted() {
9. return "mounted".equals(state);
10. }
11. @Override
12. public String toString() {
13. return "StorageInfo [path=" + path + ", state=" + state
14. ", isRemoveable=" + isRemoveable + "]";
15. }
16. }
[java] view plain copy
1. public static List<StorageInfo> listAllStorage(Context context) {
2. new ArrayList<StorageInfo>();
3. StorageManager storageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
4. try {
5. Class<?>[] paramClasses = {};
6. class.getMethod("getVolumeList", paramClasses);
7. Object[] params = {};
8. Object[] invokes = (Object[]) getVolumeList.invoke(storageManager, params);
9.
10. if (invokes != null) {
11. null;
12. for (int i = 0; i < invokes.length; i++) {
13. Object obj = invokes[i];
14. "getPath", new Class[0]);
15. new Object[0]);
16. new StorageInfo(path);
17.
18. class.getMethod("getVolumeState", String.class);
19. String state = (String) getVolumeState.invoke(storageManager, info.path);
20. info.state = state;
21.
22. "isRemovable", new Class[0]);
23. new Object[0])).booleanValue();
24. storages.add(info);
25. }
26. }
27. catch (Exception e) {
28. e.printStackTrace();
29. }
30. storages.trimToSize();
31. return storages;
32. }
33.
34. public static List<StorageInfo> getAvaliableStorage(List<StorageInfo> infos){
35. new ArrayList<StorageInfo>();
36. for(StorageInfo info : infos){
37. new File(info.path);
38. if ((file.exists()) && (file.isDirectory()) && (file.canWrite())) {
39. if (info.isMounted()) {
40. storages.add(info);
41. }
42. }
43. }
44.
45. return storages;
46. }
调用上述方法:
[java] view plain copy
1. List<StorageInfo> list = listAllStorage(this);
2. for(StorageInfo info : list){
3. Log.e(TAG, info.toString());
4. }
5. Log.e(TAG, "-----------------");
6. List<StorageInfo> infos = getAvaliableStorage(list);
7. for(StorageInfo info : infos){
8. Log.e(TAG, info.toString());
9. }
10.
11. Log.e(TAG, "Environment.getExternalStorageDirectory(): " + Environment.getExternalStorageDirectory());
连上手机进行验证,输出Log信息:
可以看到,通过listAllStorage()方法获取到了手机上的所有存储设备,通过getAvaliableStorage()方法的过滤获取到了挂载状态的所有存储设备。由于该手机只有一个可读写的存储设备,因此与Environment.getExternalStorageDirectory()方法获取到的结果一致。
===================================================================
getExternalFilesDir(null)参数传入的为null,这样默认访问的是files文件夹,我们可以指定子文件夹
getExternalFilesDir(null) 得到 "/mmn/sdcard/Android/data/< package name >/files/
”getExternalFilesDir("Caches") 得到 "
/mmn/sdcard/Android/data/< package name >/files/Caches
"
有些时候我们的手机没有安装SD卡,所以我们使用前需要判断一下:
//SD卡已装入 }