在安卓的开发中,有时候要用到大文件的存储,这个时候就不能存储在应用内部(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();
    }
    
    大功告成!