在操作MediaStore的时候会用到Uri。
如何在SD卡上保存一张图片呢?
(1)之前的方式
我们都是在SD卡下面建一个目录比如/storage/emulated/0/shvdownload/video/tempVideo,然后把把需要保存的媒体资源(图片、视频)通过File 输出流的方式写入文件。然后就不管了,让系统触发扫描的时候,把这个媒体信息放到MediaStore中;或者通过触发MediaStore扫描,看参考文章。
从Android10开始,引入了分区存储,不让直接通过File的方式操作SD卡路径了。必须通过MediaStore,也就是说,让MediaStore给你指定存放位置,暴露抽象的Uri,让你用,真实路径隐藏。
(2)通过MediaStore的方式
MediaStore其实就是一个数据库,里面存的是媒体信息(名字、描述、路径等),访问MediaStore通过ContentResolver。MediaStore存的只是媒体信息,保存媒体本身到本地还是需要我们自己完成的,只不过MediaStore告诉了我们位置,而且有写入的方式。
public Uri createImageUri(Context context) {
ContentValues values = new ContentValues();
// 需要指定文件信息时,非必须
values.put(MediaStore.Images.Media.DESCRIPTION, "This is an image");
values.put(MediaStore.Images.Media.DISPLAY_NAME, "Image.png");
values.put(MediaStore.Images.Media.MIME_TYPE, "image/png");
values.put(MediaStore.Images.Media.TITLE, "Image.png");
//values.put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/test");
return context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
}
先在MediaStore的Images中创建一条表示图片的记录,返回表示这个图片的Uri。
我们连续调用两次,看看结果都是啥
public void showMediaInfo(Context context, Uri uri) {
Cursor cursor= context.getContentResolver().query(uri, null, null, null, null);
//System.out.println("lzy cursor.getCount() = " + cursor.getCount());
cursor.moveToFirst();
String path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA));
String DESCRIPTION = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DESCRIPTION));
String DISPLAY_NAME = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME));
String MIME_TYPE = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.MIME_TYPE));
String TITLE = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.TITLE));
System.out.println("lzy path = " + path);
System.out.println("lzy DESCRIPTION = " + DESCRIPTION);
System.out.println("lzy DISPLAY_NAMEDISPLAY_NAME = " + DISPLAY_NAME);
System.out.println("lzy MIME_TYPE = " + MIME_TYPE);
System.out.println("lzy TITLE = " + TITLE);
}
2020-10-15 14:19:56.297 7041-7041/com.sohu.sohuvideo I/System.out: lzy uri = content://media/external/images/media/42389
2020-10-15 14:19:56.300 7041-7041/com.sohu.sohuvideo I/System.out: lzy path = /storage/emulated/0/Pictures/1602742795276.jpg
2020-10-15 14:19:56.300 7041-7041/com.sohu.sohuvideo I/System.out: lzy DESCRIPTION = This is an image
2020-10-15 14:19:56.301 7041-7041/com.sohu.sohuvideo I/System.out: lzy DISPLAY_NAMEDISPLAY_NAME = Image.png
2020-10-15 14:19:56.301 7041-7041/com.sohu.sohuvideo I/System.out: lzy MIME_TYPE = image/png
2020-10-15 14:19:56.301 7041-7041/com.sohu.sohuvideo I/System.out: lzy TITLE = Image.png
2020-10-15 14:19:57.317 7041-7041/com.sohu.sohuvideo I/System.out: lzy uri = content://media/external/images/media/42389
2020-10-15 14:19:57.343 7041-7041/com.sohu.sohuvideo I/System.out: lzy path = /storage/emulated/0/Pictures/1602742796302.jpg
2020-10-15 14:19:57.343 7041-7041/com.sohu.sohuvideo I/System.out: lzy DESCRIPTION = This is an image
2020-10-15 14:19:57.344 7041-7041/com.sohu.sohuvideo I/System.out: lzy DISPLAY_NAMEDISPLAY_NAME = Image.png
2020-10-15 14:19:57.344 7041-7041/com.sohu.sohuvideo I/System.out: lzy MIME_TYPE = image/png
2020-10-15 14:19:57.344 7041-7041/com.sohu.sohuvideo I/System.out: lzy TITLE = Image.png
看到没,有两条记录,MediaStore给指定了真正的路径是在/storage/emulated/0/Pictures/下面。
看看MediaStore.Images.Media.EXTERNAL_CONTENT_URI是啥
/**
* Get the content:// style URI for the image media table on the
* given volume.
*
* @param volumeName the name of the volume to get the URI for
* @return the URI to the image media table on the given volume
*/
public static Uri getContentUri(String volumeName) {
return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
"/images/media");
}
/**
* The content:// style URI for the internal storage.
*/
public static final Uri INTERNAL_CONTENT_URI =
getContentUri("internal");
/**
* The content:// style URI for the "primary" external storage
* volume.
*/
public static final Uri EXTERNAL_CONTENT_URI =
getContentUri("external");
其实就是把“external”传给了getContentUri方法。
访问共享存储空间中的媒体文件官方也给了在MediaStore写入一条的例子,我们看一下
// Add a specific media item.
ContentResolver resolver = getApplicationContext()
.getContentResolver();
// Find all audio files on the primary external storage device.
// On API <= 28, use VOLUME_EXTERNAL instead.
Uri audioCollection = MediaStore.Audio.Media.getContentUri(
MediaStore.VOLUME_EXTERNAL_PRIMARY);
// Publish a new song.
ContentValues newSongDetails = new ContentValues();
newSongDetails.put(MediaStore.Audio.Media.DISPLAY_NAME,
"My Song.mp3");
// Keeps a handle to the new song's URI in case we need to modify it
// later.
Uri myFavoriteSongUri = resolver
.insert(audioCollection, newSongDetails);
这个是Audio的,和Images是一个意思。看Uri audioCollection = MediaStore.Audio.Media.getContentUri(
MediaStore.VOLUME_EXTERNAL_PRIMARY)这句话MediaStore.VOLUME_EXTERNAL_PRIMARY和MediaStore.VOLUME_EXTERNAL是Android10(29)新加的api,分别表示external_primary和external,在之前的系统只有external,所以MediaStore.Audio.Media.getContentUri(
MediaStore.VOLUME_EXTERNAL)和MediaStore.Audio.Media.EXTERNAL_CONTENT_URI是一个意思。
external_primary的介绍看存储卷
上面只是说了,怎么在MediaStore中插入媒体信息,没有说怎么把媒体本身保存到本地。
官方介绍了两种打开媒体文件的方法,文件描述符和文件流。
而且有文章是用文件流打开的,然后把数据写入AndroidQ(10)分区存储完美适配之下载图片(文件)本地。这种方式,应该是没有问题的,比如下载视频或者图片到本地。对于视频的迁移有没有其它办法呢?
其实通过MediaStore保存共享文件的方式Android一开始就有,“分区存储”模型要求保存共享文件必须使用MediaStore的方式,使用File直接访问路径是禁止的,会报错。
三种方法,刷新 Android 的 MediaStore!让你保存的图片立即出现在相册里!