如何在MediaStore扫描到的音频文件同目录下创建新音频文件?
如何在MediaStore扫描到的音频文件同目录下创建新音频文件?
老哥我太懂你这糟心的情况了!之前我做音频裁剪工具的时候也踩过一模一样的坑——用Media3 Transformer把裁剪好的音频存到原歌曲目录了,结果刷新App的时候MediaStore死活不认这个新文件,还时不时蹦个NullPointerException出来,非得手动重启App或者去文件管理器里戳一下才正常。给你唠唠我后来搞定的办法:
一、先搞对保存路径的获取(适配Scoped Storage)
Android 10及以上用了Scoped Storage,不能直接硬写文件路径了,得从原音频的MediaStore数据里拿合法的存储位置:
- 从你查到的原音频
Uri,用ContentResolver查它的RELATIVE_PATH列,这个就是它在外部存储的相对路径(比如Music/MyCollection/),这就是咱们要存新文件的父目录。 - 先给新音频在MediaStore里插个条目,这样MediaStore从一开始就知道有这个文件,后续查询不会空:
val contentValues = ContentValues().apply { put(MediaStore.Audio.Media.DISPLAY_NAME, "剪好的-${原文件名}.mp3") // 自定义新文件名 put(MediaStore.Audio.Media.MIME_TYPE, "audio/mpeg") // 格式要对应,比如flac就改audio/flac put(MediaStore.Audio.Media.RELATIVE_PATH, 原音频的RELATIVE_PATH) // 用刚才查到的原目录路径 put(MediaStore.Audio.Media.IS_PENDING, 1) // 标记为待写入,避免MediaStore提前扫描 } // 插入后拿到可写的Uri,后续用这个Uri给Media3写文件 val newAudioUri = contentResolver.insert(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, contentValues)
二、用Media3 Transformer写入到MediaStore Uri
别再直接写文件路径了,就用上面拿到的newAudioUri让Media3写入,这样写入和MediaStore索引同步:
val transformer = Transformer.Builder(context) .setDataSourceFactory(DefaultDataSource.Factory(context)) .build() // 构建裁剪请求,替换成你自己的起始、结束时间 val editRequest = MediaItemEditRequest.Builder(原音频的MediaItem) .setTrim(裁剪开始时间毫秒值, 裁剪结束时间毫秒值) .build() // 执行裁剪并写入到MediaStore的Uri transformer.edit(editRequest, newAudioUri) .addListener({ // 写入成功后,把IS_PENDING改成0,让MediaStore正式索引这个文件 contentValues.clear() contentValues.put(MediaStore.Audio.Media.IS_PENDING, 0) newAudioUri?.let { uri -> contentResolver.update(uri, contentValues, null, null) } // 这时候再刷新MediaStore查询,肯定能拿到新文件信息,不会NPE了 }, Executors.newSingleThreadExecutor())
三、如果已经直接写了文件,怎么补救
要是你之前已经硬写了文件到原目录,那得手动通知MediaScanner扫描这个新文件,不然MediaStore不知道它存在,查的时候就返回null炸NPE:
// 替换成你实际的新文件绝对路径 val newFilePath = "/storage/emulated/0/Music/MyCollection/剪好的歌曲.mp3" MediaScannerConnection.scanFile( context, arrayOf(newFilePath), arrayOf("audio/mpeg") // 对应你的音频MIME类型 ) { path, uri -> // 扫描完成后,这里能拿到新文件的MediaStore Uri,之后查询就没问题了 }
为啥会碰NPE?
说白了就是你写完文件后,MediaStore还没来得及扫描索引这个新文件,你就急着去查询,结果查到的是null,直接用就炸了。要么在写入前就通过MediaStore创建条目,要么写完后主动触发扫描,等扫描完再刷新数据。
对了,权限别忘了!Android 13+要申请READ_MEDIA_AUDIO和WRITE_MEDIA_AUDIO,低版本可能需要WRITE_EXTERNAL_STORAGE,但尽量用MediaStore的方式,少要全存储权限,合规性更好。
备注:内容来源于stack exchange,提问作者Vanja Vujovic
相关产品推荐
相关产品推荐

