Android 8及以下MediaStore保存音频至VoiceChanger目录失败求助
Android 8及以下版本MediaStore保存音频失败的修复方案
你的代码在Android 10+正常工作,但低版本出问题主要是几个核心错误:
问题根源
- DATA字段赋值错误:低版本
MediaStore.Audio.Media.DATA需要传入完整的文件路径(包含文件名),你只传了目录路径,导致MediaStore无法定位具体文件。 - getContentUri调用错误:低版本不需要通过字符串构建content uri,直接使用
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI即可,你的写法会导致uri格式错误。 - savedPath逻辑错误:你把原缓存文件路径赋值给了savedPath,而非实际保存到VoiceChanger目录的新文件路径。
- 缺少媒体库扫描:低版本写入文件后需要主动通知系统扫描,否则音乐应用无法立即看到新文件。
修复后的代码
suspend fun String.saveAudioToGallery( context: Context, mimeType: String = "audio/x-wav", done: (String?) -> Unit, error: () -> Unit ) = withContext(Dispatchers.IO) { var savedPath: String? = null val fileName = "Vc_${System.currentTimeMillis()}.wav" val contentValues = ContentValues().apply { put(MediaStore.Audio.Media.DISPLAY_NAME, fileName) put(MediaStore.Audio.Media.MIME_TYPE, mimeType) put(MediaStore.Audio.Media.DATE_ADDED, System.currentTimeMillis() / 1000) put(MediaStore.Audio.Media.DATE_MODIFIED, System.currentTimeMillis() / 1000) } val collection: Uri val targetFile: File if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { contentValues.put( MediaStore.Audio.Media.RELATIVE_PATH, "${Environment.DIRECTORY_MUSIC}/VoiceChanger" ) collection = MediaStore.Audio.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY) targetFile = File( Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC), "VoiceChanger/$fileName" ) } else { val directory = File( Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC), "VoiceChanger" ) if (!directory.exists()) { directory.mkdirs() } targetFile = File(directory, fileName) // 低版本必须传入完整文件路径到DATA字段 contentValues.put(MediaStore.Audio.Media.DATA, targetFile.absolutePath) // 低版本直接使用EXTERNAL_CONTENT_URI collection = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI } val contentResolver = context.contentResolver try { val audioUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { contentResolver.insert(collection, contentValues) } else { // 低版本先创建空文件,再插入MediaStore if (targetFile.createNewFile()) { contentResolver.insert(collection, contentValues) } else { null } } Log.d("inputStram", "saveAudioToGalleryUri:$audioUri ") audioUri?.let { uri -> contentResolver.openOutputStream(uri)?.use { os -> val sourceFile = File(this@saveAudioToGallery) FileInputStream(sourceFile).use { inputStream -> inputStream.copyTo(os) savedPath = targetFile.absolutePath // 改为目标文件路径 Log.d("inputStram", "saveAudioToGallery success: $savedPath") } } // 低版本通知媒体库扫描新文件 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { MediaScannerConnection.scanFile( context, arrayOf(targetFile.absolutePath), arrayOf(mimeType) ) { _, _ -> Log.d("inputStram", "Media scanned successfully") } } } } catch (e: Exception) { Log.e("TAG", "saveAudioToGallery failed: ${e.message}", e) } withContext(Dispatchers.Main) { if (savedPath != null) { Log.d("TAGGing", "saveAudioToGallery completed: $savedPath") done(savedPath) } else { Toast.makeText(context.applicationContext, "Failed to save audio to gallery", Toast.LENGTH_SHORT).show() error() } } }
关键修改说明
- 统一文件名处理:抽离fileName变量,确保高低版本使用同一文件名,避免路径混乱。
- 低版本DATA字段修复:传入
targetFile.absolutePath完整文件路径,而非目录路径。 - 纠正content uri获取:低版本直接使用
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,避免错误的uri构建。 - 修正savedPath赋值:改为实际保存的目标文件路径,而非原缓存文件路径。
- 添加媒体扫描:低版本调用
MediaScannerConnection.scanFile通知系统更新媒体库,让音乐应用能立即识别新文件。 - 优化流操作:使用
use块自动管理流的关闭,避免手动关闭引发的异常。
另外,需在AndroidManifest.xml中添加对应权限:
<!-- 低版本需要外部存储写入权限 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
针对Android 6.0+到8.1的设备,还需动态申请WRITE_EXTERNAL_STORAGE权限。
内容的提问来源于stack exchange,提问作者Muhammad Awais Shan
相关产品推荐
相关产品推荐

