fix: 语音改用 OkHttp 下载后本地播放

MediaPlayer 内置 HTTP 在 Android 8.1 设备请求 HTTPS 失败
(NuCachedSource2 error -1)。改为 OkHttp 先下载到缓存再播放

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
dongliang
2026-05-07 11:18:09 +09:30
parent b9d50e3c31
commit f22f7b7f0f

View File

@@ -406,28 +406,70 @@ class TaskListFragment : BaseFragment<FragmentTaskListBinding>() {
stopVoice() // 清理上一个 stopVoice() // 清理上一个
updateVoiceUI(true) updateVoiceUI(true)
Timber.d("语音播放: $url") Timber.d("语音播放: $url")
mediaPlayer = MediaPlayer().apply { viewLifecycleOwner.lifecycleScope.launch {
setAudioAttributes( try {
// 先用 OkHttp 下载到缓存MediaPlayer 内置 HTTP 在部分设备 HTTPS 有问题)
val cacheFile = kotlinx.coroutines.withContext(kotlinx.coroutines.Dispatchers.IO) {
downloadToCache(url)
}
if (cacheFile == null) {
Timber.w("语音下载失败: $url")
stopVoice()
return@launch
}
// 从本地文件播放
val player = MediaPlayer()
player.setAudioAttributes(
android.media.AudioAttributes.Builder() android.media.AudioAttributes.Builder()
.setContentType(android.media.AudioAttributes.CONTENT_TYPE_SPEECH) .setContentType(android.media.AudioAttributes.CONTENT_TYPE_SPEECH)
.setUsage(android.media.AudioAttributes.USAGE_MEDIA) .setUsage(android.media.AudioAttributes.USAGE_MEDIA)
.build() .build()
) )
setDataSource(url) player.setDataSource(cacheFile.absolutePath)
setOnPreparedListener { player.setVolume(1.0f, 1.0f)
Timber.d("语音准备完成,开始播放") player.setOnCompletionListener { stopVoice() }
start() player.setOnErrorListener { _, what, extra ->
} Timber.w("语音播放错误: what=$what, extra=$extra")
setOnCompletionListener { stopVoice() }
setOnErrorListener { _, what, extra ->
Timber.w("语音播放失败: what=$what, extra=$extra, url=$url")
stopVoice() stopVoice()
true true
} }
prepareAsync() player.prepare()
mediaPlayer = player
player.start()
Timber.d("语音开始播放, duration=${player.duration}ms")
} catch (e: Exception) {
Timber.e(e, "语音播放失败: $url")
stopVoice()
} }
} }
} }
}
/** 用 OkHttp 下载音频文件到缓存目录 */
private fun downloadToCache(url: String): java.io.File? {
return try {
val client = okhttp3.OkHttpClient()
val request = okhttp3.Request.Builder().url(url).build()
val response = client.newCall(request).execute()
if (!response.isSuccessful) {
Timber.w("语音下载HTTP错误: ${response.code}")
return null
}
// 写入缓存文件
val fileName = "voice_${url.hashCode()}.mp3"
val cacheFile = java.io.File(requireContext().cacheDir, fileName)
response.body?.byteStream()?.use { input ->
cacheFile.outputStream().use { output ->
input.copyTo(output)
}
}
Timber.d("语音下载完成: ${cacheFile.length()} bytes")
cacheFile
} catch (e: Exception) {
Timber.e(e, "语音下载异常")
null
}
}
/** 停止并释放 MediaPlayer恢复按钮状态 */ /** 停止并释放 MediaPlayer恢复按钮状态 */
private fun stopVoice() { private fun stopVoice() {