在安卓的开发中,有时候要用到大文件的存储,这个时候就不能存储在应用内部(data/data),只能借助外部存储。而外部存储又分为手机机身的存储空间(一级sd卡)和外置sd卡存储空间(二级sd卡)。
1.要存储首先是获得读写权限
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />//操作文件
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />//向sd卡写入
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />//读取sd卡信息
2.接下来是获得外存储路径
我们一般会想到用Environment.getExternalStorageDirectory().getAbsolutePath(),但是由于国内手机系统各种定制,各个厂
商都做过了修改。Environment.getExternalStorageDirectory().getAbsolutePath()的意思是获得外部存储的路径,各个厂商自己认为的外部存储可能不一样,所以这个方法能调用到的路劲可能也有所差别。怎么解决呢?安卓源代码里面有一个类专门管理外部存储,那就是StorageManager。调用storageManager.getStorageVolumes()就能获得所有外置sd卡的挂载情况。但是这个方法要7.0或者以上才能调用。所以我们考虑使用反射。
public static ArrayList<StorageInfo> getAvaliableStorages(Context context) {
ArrayList<StorageInfo> storagges = new ArrayList<>();
StorageManager storageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
try {
Class<?>[] paramClasses = {};
Method getVolumeList = StorageManager.class.getMethod("getVolumeList", paramClasses);
getVolumeList.setAccessible(true);
Object[] params = {};
Object[] invokes = (Object[]) getVolumeList.invoke(storageManager, params);
if (invokes != null) {
StorageInfo info = null;
for (int i = 0; i < invokes.length; i++) {
Object obj = invokes[i];
Method getPath = obj.getClass().getMethod("getPath", new Class[0]);
String path = (String) getPath.invoke(obj, new Object[0]);
info = new StorageInfo(path);
File file = new File(info.path);
if ((file.exists()) && (file.isDirectory()) && (file.canWrite())) {
Method isRemovable = obj.getClass().getMethod("isRemovable", new Class[0]);
String state = null;
try {
Method getVolumeState = StorageManager.class.getMethod("getVolumeState", String.class);
state = (String) getVolumeState.invoke(storageManager, info.path);
info.state = state;
} catch (Exception e) {
e.printStackTrace();
}
if (info.isMounted()) {
info.isRemoveable = ((Boolean) isRemovable.invoke(obj, new Object[0])).booleanValue();
storagges.add(info);
}
}
}
}
} catch (NoSuchMethodException e1) {
e1.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
storagges.trimToSize();
return storagges;
}
public class StorageInfo {
public String path;
public String state;
public boolean isRemoveable;
public StorageInfo(String path) {
this.path = path;
}
public boolean isMounted() {
return "mounted".equals(state);
}
}
这样便得到了所有的外置存储卡的信息。我的手机信息是
机身存储 /storage/emulated/0/1
外置sd卡 /storage/sdcard1/
3.文件写入
我们只要调用storageInfo.isRemoveable 可移除的就是二级存储卡,一般就是我们可拔插的sd卡。不可移除的就是机身存储。
调用storageInfo.isMounted()就可知道这个存储位置是否可用。
(1)机身存储位置的文件写入
这个没什么好讲,只要获得路径便可直接写入
(2)外置sd卡的文件写入
在4.4以前,外置sd卡也是可以直接写入的,但是在4.4以后Android开始有了多卡存储。于是拓展的sd卡,便没有了写入权限。
据说是为了安卓系统可以在应用卸载之后可以轻松的删掉本地文件。
但是,Android并不是禁止了sd卡所有的写入权限。在特定的目录下,我们还是可以执行写入操作的。那就是在sd卡的
Android/data/"+context.getPackageName()+"/files/ 路径下,允许用户写入文件。这就可以理解了,特定的sd卡目录在 应用卸载之后会被清除文件,而至于机身自带的存储空间要清除残留文件肯定是没问题的。这样就可以在一定程度上缓解安卓机 越用越卡的毛病了(残留文件都被系统删除了)。 所以,如果我们要在外置sd卡写入文件的话,只要写在这个路径下就可以了,我的是 /storage/sdcard1/Android/data/"+context.getPackageName()+"/files/ 需要注意的是,在创建这个路径的时候要先调用context.getExternalFilesDir(null).getAbsolutePath(),一定要先调用这一句 否则文件不能写入,这句话大概就是创建固定路径。
context.getExternalFilesDir(null).getAbsolutePath();//要先执行此方法,创建sd卡目录,否则不能创建目录
String direction = "/storage/sdcard1/Android/data/"+context.getPackageName()+"/files/ "; File file = new File(direction);
if (!file.exists()){
file.mkdirs();
}
大功告成!