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,29 +406,71 @@ class TaskListFragment : BaseFragment<FragmentTaskListBinding>() {
stopVoice() // 清理上一个 stopVoice() // 清理上一个
updateVoiceUI(true) updateVoiceUI(true)
Timber.d("语音播放: $url") Timber.d("语音播放: $url")
mediaPlayer = MediaPlayer().apply { viewLifecycleOwner.lifecycleScope.launch {
setAudioAttributes( try {
android.media.AudioAttributes.Builder() // 先用 OkHttp 下载到缓存MediaPlayer 内置 HTTP 在部分设备 HTTPS 有问题)
.setContentType(android.media.AudioAttributes.CONTENT_TYPE_SPEECH) val cacheFile = kotlinx.coroutines.withContext(kotlinx.coroutines.Dispatchers.IO) {
.setUsage(android.media.AudioAttributes.USAGE_MEDIA) downloadToCache(url)
.build() }
) if (cacheFile == null) {
setDataSource(url) Timber.w("语音下载失败: $url")
setOnPreparedListener { stopVoice()
Timber.d("语音准备完成,开始播放") return@launch
start() }
} // 从本地文件播放
setOnCompletionListener { stopVoice() } val player = MediaPlayer()
setOnErrorListener { _, what, extra -> player.setAudioAttributes(
Timber.w("语音播放失败: what=$what, extra=$extra, url=$url") android.media.AudioAttributes.Builder()
.setContentType(android.media.AudioAttributes.CONTENT_TYPE_SPEECH)
.setUsage(android.media.AudioAttributes.USAGE_MEDIA)
.build()
)
player.setDataSource(cacheFile.absolutePath)
player.setVolume(1.0f, 1.0f)
player.setOnCompletionListener { stopVoice() }
player.setOnErrorListener { _, what, extra ->
Timber.w("语音播放错误: what=$what, extra=$extra")
stopVoice()
true
}
player.prepare()
mediaPlayer = player
player.start()
Timber.d("语音开始播放, duration=${player.duration}ms")
} catch (e: Exception) {
Timber.e(e, "语音播放失败: $url")
stopVoice() stopVoice()
true
} }
prepareAsync()
} }
} }
} }
/** 用 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() {
mediaPlayer?.let { mediaPlayer?.let {