Android 11(R)的分区存储

一、Android 存储方式的演变
1、随着Android版本的升级,私有目录(/data/data/package/)的存储方式没有发生改变,当应用卸载之后,这部分的内容也随之删除,变化的是外置卡的存储方式,下面我们讲的内容都是针对外置卡的存储方式而言。
2、Android 9 外置卡存储方式,每个应用都可以访问,包括增删改查(当然这个APP已经申请了读写外置卡的权限)。
3、Android 10 对分区存储做了一个过渡,你可以选择分区存储也可以不选,如果在Application的节点中设置这个属性为true,requestLegacyExternalStorage=“true” 则代表不使用分区存储,就和Android 9 一样。
4、Android 11 强制进行分区存储,也是就是对每个文件夹存储那些文件内容都做了规范,比如Download目录下可以存放任何的文件,Movies(存放 MP4视频类型的文件)、Music(存放mp3音乐类型的)、Picture(存放 png、jpg图片类型的)、Documents(存放文档 txt word 等)
二、Android 存储权限的讲解
媒体文件集指的是图片、音频、视频文件
1、Android 10 中,自己的 App 无需任何权限(这里说的权限也包括 WRITE_EXTERNAL_STORAGE 和 READ_EXTERNAL_STORAGE)就能向媒体集添加文件,也可以编辑和删除自己添加的媒体文件。
如果你要读取并操作非自己应用创建的媒体文件,就需要读写外部存储权限,如果用户没有同意,你将无法编辑和删除并非自己创建的媒体文件。
2、下载文件集
用于和其他应用分享非媒体文件
你无需添加任何权限,就可以编辑和删除自己添加的非媒体文件。
与媒体不同的是,即使有读取外部存储权限,也不允许访问由其他应用创建的非媒体文件。想要获此权限,必须通过调用 Storage Access Framerwork API,启动系统文件选择器,让用户进行选择访问那些文件和目录。
三、具体的实例代码演示
Android 9 之前创建文件方式

 public void createOther(View view) {
        // 这个就是一个文件目录
        File file = new File("/sdcard/lgj.txt");
        if (!file.exists()) {
            try {
                // 创建文件
                file.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

Android 11 创建文件的方式

// Android R /data/data/com.android.providers.media/databases/external.db 专门管理各种文件数据库的
    // 有一张表,files 专门管理所有文件的数据表(只有当你选择分区存储的时候,才这么玩,包含 文件,图片,视频
    // Android R 操作文件的方式,这种方式在Android 10 想要兼容,必须设置 requestLegacyExternalStorage="false"
    public void createOn11(View view) {
        // MediaStore.Files 这个是放置,所有的文件夹(包括Documents、Movies、Picture等)
        // Android 11 操作文件就是操作数据库的,获取到一个路径
        Uri uri = MediaStore.Files.getContentUri("external");
        // 创建一个ContentValue对象,用来插入数据库(给存储文件的数据)
        ContentValues contentValues = new ContentValues();
        // 直接创建 lgj.txt 要存储的路径,要创建的文件的上一级存储目录
        String path = Environment.DIRECTORY_DOWNLOADS + "lgj";// 这个是文件夹
        // 给路径的字段设置键值对
        contentValues.put(MediaStore.Downloads.RELATIVE_PATH,path);
        // 设置文件的名字,如果数据库已经有了这个文件,有可能存储不成功
        contentValues.put(MediaStore.Downloads.DISPLAY_NAME,"lgj.txt");
        // 可以不用设置
        contentValues.put(MediaStore.Downloads.TITLE,"lgj");
        // 第一个参数是,操作那个数据库,因为我们操作的是 external 数据库,所以直接传递上面的那个uri就行
        // 插入一条数据,然后把生成的这个文件的路径返回回来
        Uri insert = getContentResolver().insert(uri, contentValues);
        // 在Android 11 上,所有的文件和文件夹从名字上是无法区分的,除非你这个文件里面是有内容,才能真正成为文件
        String content = "我还是我,不一样的烟火";
        OutputStream outputStream = null;
        try {
            // 这块是往这个文件中写入数据,那么他才会真正的成为一个文件,否则就是文件夹
            outputStream = getContentResolver().openOutputStream(insert);
            BufferedOutputStream bos = new BufferedOutputStream(outputStream);
            bos.write(content.getBytes());
            bos.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

创建图片以及对图片的增删改查

public void createImg(View view){
        String imgName = "mv.jpg";
        ContentResolver contentResolver = getContentResolver();
        ContentValues contentValues = new ContentValues();
        contentValues.put(MediaStore.Images.ImageColumns.RELATIVE_PATH,Environment.DIRECTORY_PICTURES);
        contentValues.put(MediaStore.Images.ImageColumns.DISPLAY_NAME,imgName);
        //图片需要多传一个参数,声明图片的类型,
        contentValues.put(MediaStore.Images.ImageColumns.MIME_TYPE,"image/jpg");
        contentValues.put(MediaStore.Downloads.TITLE,imgName);
        // 插入外置卡路径
        Uri uri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
        // 所有文件的创建都需要写数据,如果不写数据那么就生成不了文件
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher_background);
        try {
            OutputStream outputStream = contentResolver.openOutputStream(uri);
            bitmap.compress(Bitmap.CompressFormat.JPEG,100,outputStream);
            outputStream.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    Uri queryUri;
    public void queryImg(){
        // 获取外置卡专门存放图片的URI
        Uri externalContentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
        String selection = MediaStore.Images.Media.DISPLAY_NAME +"=?";
        String[] arg = new String[]{"mv.jpg"};
        // 第二个参数是指定查询的列,如果为null,那么就查询所有
        Cursor cursor = getContentResolver().query(externalContentUri, null, selection, arg, null);
        if (cursor != null && cursor.moveToFirst()){
            // 这是获取图片存储的绝对路径,
            // int columnIndexOrThrow = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
            // String data = cursor.getString(columnIndexOrThrow);

            // 我要获取这张图片的URI,我先拿到这个图片的id
            long id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID));
            // 将我们查询的 id 转化成对应的URI,
            queryUri = ContentUris.withAppendedId(externalContentUri, id);
            cursor.close();
        }
    }

    public void deleteImg(){
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) {
            int delete = getContentResolver().delete(queryUri, null);
            if (delete > 0){
                Toast.makeText(this,"删除成功",Toast.LENGTH_SHORT).show();
            }
        }
    }

    public void updateImg(){
        ContentValues contentValues = new ContentValues();
        contentValues.put(MediaStore.Images.ImageColumns.DISPLAY_NAME,"美女.jpg");
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) {
            int update = getContentResolver().update(queryUri, null, null);
            if (update > 0){
                Toast.makeText(this,"修改成功",Toast.LENGTH_SHORT).show();
            }
        }
    }
    // 动态检查权限
    public static boolean checkPermission(Activity activity) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
                activity.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
                        != PackageManager.PERMISSION_GRANTED) {
            activity.requestPermissions(new String[]{
                    Manifest.permission.WRITE_EXTERNAL_STORAGE,
                    Manifest.permission.READ_EXTERNAL_STORAGE
            },1);
        }
        return false;
    }

后续代码继续更新,应该把这些繁琐的操作写成框架

你可能感兴趣的:(Android 11(R)的分区存储)