Android 10开发之 保存、读取图片
概述
从Android 10(Q)开始,谷歌就开始修改了外部存储权限,叫做分区存储,分区存储可以分为两个目录,分别是 沙盒目录(App-specific directory 和 公共目录(Public Directory)
沙盒目录
沙盒目录存储在 /Android/data/包名,保存文件到该目录,一般通过 Context.getExternalFilesDir() ,例如:context.getExternalFilesDir(Environment.DIRECTORY_PICTURES) 表示路径为:/Android/data/包名/Pictures/,app一旦卸载,沙盒目录下的文件都会被删除。Android 10以上去除了WRITE_EXTERNAL_STORAGE权限,不需要这个权限就可以保存文件到沙盒目录
公共目录
公共目录包括 多媒体目录 和 下载目录
公共目录的媒体文件(Photos, Images, Videos, Audio)通过MediaStore来访问,另外,MediaStore的DATA字段从Android 10开始被标记为deprecated,通过该字段获取的文件路径不再可靠,Android 10以上新增字段RELATIVE_PATH,代表文件的相对路径,在使用MediaStore保存媒体文件时,可以通过设置该字段来设置媒体文件保存的文件夹
保存文件(以图片为例)
沙盒目录
public static String FileSaveToInside(Context context, String fileName, Bitmap bitmap) {
FileOutputStream fos = null;
String path = null;
try {
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;
}
保存文件到沙盒目录时,操作简单,不需要判断Android版本做兼容
公共目录
public static String fileSaveToPublic(Context context, String fileName, Bitmap bitmap) {
String path = null;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
FileOutputStream fos = null;
try {
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 {
String folder = Environment.DIRECTORY_PICTURES;
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);
Uri uri = context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
OutputStream os = null;
try {
if (uri != null) {
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;
}
Android10开始,访问公共目录的方式有改变,通过 MediaStore 来访问,因为不能完全保证Android10以下的手机能通过MediaStore方式保存文件,因此在保存文件时,根据Android版本分成两种情况(Android10以下、Android10及以上),这样稳妥
读取文件(以图片为例)
public static Uri FileGetFromPublic(Context context, String filePath, String fileName) {
String queryPath;
if (!filePath.endsWith("/")) {
filePath = filePath + File.separator;
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
filePath = filePath + fileName;
queryPath = MediaStore.Images.Media.DATA;
} else {
queryPath = MediaStore.Images.Media.RELATIVE_PATH;
}
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()) {
int id = cursor.getInt(cursor.getColumnIndex(MediaStore.Images.Media._ID));
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字段:
|