当你的机器Build.VERSION.SDK_INT > Build.VERSION_CODES.Q
的时候,你会发现刷新相册的方法不好使了,因为30的机器上,已经不允许直接发广播刷目录就在相册显示了,取而代之的是,他用了类似苹果那种,先向系统申请一个uri ,再进行拷贝写入的方法,步骤如下,
① 获取系统图片视频媒体的根Uri
需要用MediaStore.Video.Media去获取一个uri(图片的可以用MediaStore.Image.Media), 图片和视频的是分开的,额,好麻烦
val mediaUri = MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
② 用这个uri向系统contentresolver中插入,获取一个属于你自己文件的uri
val localUri = resolver.insert(mediaUri, values)
这里的 values 就是你插入到系统媒体数据库中的contentvalue 键值对。contentValue 有两个重要的项MediaStore.Video.Media.RELATIVE_PATH
和MediaStore.MediaColumns.IS_PENDING
RELATIVE_PATH
这里有个潜规则,就是你通过MediaStore.Video.Media或者MediaStore.Image.Media拿到的这个uri,有权限的的目录只有三个, [DCIM, Movies, Pictures], 就是你手机根目录下的这三个文件夹, 指定其他目录会报异常, 如果不指定,视频默认到Movies 下,图片默认放到Pictures下。
是代表你存取的相对路径地址,相对那三个公共目录比如(Environment.DIRECTORY_DCIM +
File.separator + “你的目录”), 实际上就是前面提到的三个目录的字符串,如果你用不是这三个目录,马上报异常,
IS_PENDING
是否存取的时候独占 1 独占, 0非独占,因为这有一个io过程,这也提醒你,在高本版机器上,还是需要再io线程完成这个动作,
③通过这个uri, 拿到一个outputStream,向系统库写入。
resolver.openOutputStream(localUri)?.use { outputStream ->
val sink = outputStream.sink().buffer()
sink.writeAll(videoFile.source().buffer())
sink.flush()
values.clear()
values.put(MediaStore.Video.Media.IS_PENDING, 0)
resolver.update(localUri, values, null, null)
}
写入之完成之后,还可以resolver.update(localUri, values, null, null) ,你才可以在相册看到你存的东东 (比如微信里面的视频,写入完成后,就被写到三个目录中的Picture下的weixin 目录下了)
下面用的是okio,use是kotlin的扩展函数, 类似let,但是只能用在实现Closeable接口的对象上,默认好多io的都可以用,感觉更方便一些,等这些都弄完后,你才能看到视频出现在相册,好恶心。
低版本上还是可以用老办法,发广播
val resolver = context.contentResolver
val values = getVideoContentValues(videoFile, System.currentTimeMillis())
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) {
val mediaUri = MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
val localUri = resolver.insert(mediaUri, values)
localUri?.let {
resolver.openOutputStream(localUri)?.use { outputStream ->
val sink = outputStream.sink().buffer()
sink.writeAll(videoFile.source().buffer())
sink.flush()
values.clear()
values.put(MediaStore.Video.Media.IS_PENDING, 0)
resolver.update(localUri, values, null, null)
}
}
}
fun getVideoContentValues(paramFile: File, currentTime: Long): ContentValues {
val values = ContentValues()
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) {
values.put(MediaStore.Video.Media.DATE_TAKEN, currentTime)
values.put(
MediaStore.Video.Media.RELATIVE_PATH,
DirConstants.videoSaveRelativePath
)
values.put(MediaStore.MediaColumns.IS_PENDING, 1)
} else {
values.put(MediaStore.Video.Media.DATA, paramFile.absolutePath)
}
values.put(MediaStore.Video.Media.TITLE, paramFile.name)
values.put(MediaStore.Video.Media.DISPLAY_NAME, paramFile.name)
values.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4")
values.put(MediaStore.Video.Media.SIZE, paramFile.length())
values.put(MediaStore.Video.Media.DATE_ADDED, currentTime)
values.put(MediaStore.Video.Media.DATE_MODIFIED, currentTime)
return values
}