Android保存、读取图片——区分Android10以下版本和Android10以上版本


1、概述

从Android 10(Q)开始,谷歌就开始修改了外部存储权限,叫做 分区存储
分区存储可以分为两个目录,分别是 沙盒目录(App-specific directory) 和 公共目录(Public Directory)。


2、沙盒目录

沙盒目录存储在 /Android/data/包名,保存文件到该目录,一般通过 Context.getExternalFilesDir()
例如:context.getExternalFilesDir(Environment.DIRECTORY_PICTURES) 表示路径为:/Android/data/包名/Pictures/,

app一旦卸载,沙盒目录下的文件都会被删除。Android 10以上去除了WRITE_EXTERNAL_STORAGE权限,不需要这个权限就可以保存文件到沙盒目录。


Context.getExternalFilesDir()方法与返回的路径对照表

在这里插入图片描述

3、公共目录

公共目录包括 多媒体目录下载目录
公共目录的媒体文件(Photos, Images, Videos, Audio)通过MediaStore来访问,另外,MediaStore的DATA字段从Android 10开始被标记为deprecated,通过该字段获取的文件路径不再可靠,Android 10以上新增字段RELATIVE_PATH,代表文件的相对路径,在使用MediaStore保存媒体文件时,可以通过设置该字段来设置媒体文件保存的文件夹


4、保存文件(图片为例)

4.1、保存文件——沙盒目录

保存文件到沙盒目录时,操作简单,不需要判断Android版本做兼容

	/**
     * 保存图片到沙盒目录
     * @param context 上下文
     * @param fileName 文件名
     * @param bitmap 文件
     * @return 路径,为空时表示保存失败
     */
    public static String FileSaveToInside(Context context, String fileName, Bitmap bitmap) {
    
    
        FileOutputStream fos = null;
        String path = null;
        try {
    
    
            //设置路径 /Android/data/com.panyko.filesave/Pictures/
            File folder = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
            //判断目录是否存在
            //目录不存在时自动创建
            if (folder.exists() ||folder.mkdir()) {
    
    
                File file = new File(folder, fileName);
                fos = new FileOutputStream(file);
                //写入文件
                bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
                fos.flush();
                path = file.getAbsolutePath();
            }
        } catch (Exception e) {
    
    
            e.printStackTrace();

        } finally {
    
    
            try {
    
    
                if (fos != null) {
    
    
                    //关闭流
                    fos.close();
                }
            } catch (IOException e) {
    
    
                e.printStackTrace();
            }

        }
        //返回路径
        return path;
    }


4.2、保存文件——公共目录

Android10开始,访问公共目录的方式有改变,通过 MediaStore 来访问,因为不能完全保证Android10以下的手机能通过MediaStore方式保存文件,因此在保存文件时,根据Android版本分成两种情况(Android10以下、Android10及以上),这样稳妥

	/**
     * 保存文件到公共目录
     * @param context 上下文
     * @param fileName 文件名
     * @param bitmap 文件
     * @return 路径,为空时表示保存失败
     */
    public static String fileSaveToPublic(Context context, String fileName, Bitmap bitmap) {
    
    
        String path = null;

        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
    
    
            //Android 10以下版本
            FileOutputStream fos = null;
            try {
    
    
                //设置路径 Pictures/
                File folder = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
                //判断目录是否存在
                //目录不存在时自动创建
                if (folder.exists() || folder.mkdir()) {
    
    
                    File file = new File(folder, fileName);
                    fos = new FileOutputStream(file);
                    //写入文件
                    bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
                    fos.flush();
                    path = file.getAbsolutePath();
                }
            } catch (IOException e) {
    
    
                e.printStackTrace();
            } finally {
    
    
                if (fos != null) {
    
    
                    try {
    
    
                        fos.close();
                    } catch (IOException e) {
    
    
                        e.printStackTrace();
                    }
                }
            }
        } else {
    
    
            //Android 10及以上版本
            
            //设置路径 Pictures/
            String folder = Environment.DIRECTORY_PICTURES;
            //设置保存参数到ContentValues中
            ContentValues values = new ContentValues();
            //设置图片名称
            values.put(MediaStore.Images.Media.DISPLAY_NAME, fileName);
            //设置图片格式
            values.put(MediaStore.Images.Media.MIME_TYPE, "image/png");
            //设置图片路径
            values.put(MediaStore.Images.Media.RELATIVE_PATH, folder);
            //执行insert操作,向系统文件夹中添加文件
            //EXTERNAL_CONTENT_URI代表外部存储器,该值不变
            Uri uri = context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
            OutputStream os = null;
            try {
    
    
                if (uri != null) {
    
    
                    //若生成了uri,则表示该文件添加成功
                    //使用流将内容写入该uri中即可
                    os = context.getContentResolver().openOutputStream(uri);
                    bitmap.compress(Bitmap.CompressFormat.PNG, 100, os);
                    os.flush();
                    path = uri.getPath();
                }
            } catch (IOException e) {
    
    
                e.printStackTrace();
            } finally {
    
    
                if (os != null) {
    
    
                    try {
    
    
                        os.close();
                    } catch (IOException e) {
    
    
                        e.printStackTrace();
                    }
                }
            }
        }
        return path;
    }


5、读取文件(图片为例)

	/**
     * 根据路径和名字查出文件
     * @param context 上下文
     * @param filePath 文件路径
     * @param fileName 文件名
     * @return
     */
    public static Uri FileGetFromPublic(Context context, String filePath, String fileName) {
    
    
        String queryPath;
        //判断是否有加斜杠
        if (!filePath.endsWith("/")) {
    
    
            filePath = filePath + File.separator;
        }
        //判断Android版本
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
    
    
            //Android10以下
            //加上文件名
            filePath = filePath + fileName;
            //使用DATA字段做查询
            queryPath = MediaStore.Images.Media.DATA;
        } else {
    
    
            //Android10及以上
            //使用RELATIVE_PATH字段做查询
            queryPath = MediaStore.Images.Media.RELATIVE_PATH;
        }
        //拼接查询条件
        //queryPath表示文件所在路径
        //DISPLAY_NAME表示文件名
        String selection = queryPath + "=? and " + MediaStore.Images.Media.DISPLAY_NAME + "=?";
        Cursor cursor = context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new String[]{
    
    MediaStore.Files.FileColumns._ID}, selection, new String[]{
    
    filePath, fileName}, null);

        if (cursor != null && cursor.moveToFirst()) {
    
    
            //查出id
            int id = cursor.getInt(cursor.getColumnIndex(MediaStore.Images.Media._ID));
            //根据id查询URI
            Uri uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id);
            return uri;
        }
        //关闭查询
        if (cursor != null) {
    
    
            cursor.close();
        }
        return null;
    }

Android 10以下是可以通过 MediaStore.Images.Media.DATA 获取文件绝对路径,Android 10 及以上DATA字段被弃用,增加了 MediaStore.Images.Media.RELATIVE_PATH获取文件相对路径。这里根据版本判断,当Android10以下时,查询DATA字段;当Android10及以上时,查询RELATIVE_PATH字段,再获取id,根据id获取URI

这里要注意下:
如果是Android10及以上的,通过RELATIVE_PATH查询时,路径后面一定要检查是否加了斜杠,如果没加,一定要加斜杠;
如果是Android10以下的,通过DATA查询时,路径一定要完整(包括文件名),我就是因为这个原因,一直查不到数据,后面把media数据库导出来,查看了一下才知道。

Android9,data字段:
在这里插入图片描述
Android11,relative_path字段:
在这里插入图片描述


转载来源:Android 10开发之 保存、读取图片

猜你喜欢

转载自blog.csdn.net/fenglolo/article/details/131537478