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:
@@ -406,28 +406,70 @@ class TaskListFragment : BaseFragment<FragmentTaskListBinding>() {
|
||||
stopVoice() // 清理上一个
|
||||
updateVoiceUI(true)
|
||||
Timber.d("语音播放: $url")
|
||||
mediaPlayer = MediaPlayer().apply {
|
||||
setAudioAttributes(
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
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()
|
||||
.setContentType(android.media.AudioAttributes.CONTENT_TYPE_SPEECH)
|
||||
.setUsage(android.media.AudioAttributes.USAGE_MEDIA)
|
||||
.build()
|
||||
)
|
||||
setDataSource(url)
|
||||
setOnPreparedListener {
|
||||
Timber.d("语音准备完成,开始播放")
|
||||
start()
|
||||
}
|
||||
setOnCompletionListener { stopVoice() }
|
||||
setOnErrorListener { _, what, extra ->
|
||||
Timber.w("语音播放失败: what=$what, extra=$extra, url=$url")
|
||||
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
|
||||
}
|
||||
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,恢复按钮状态 */
|
||||
private fun stopVoice() {
|
||||
|
||||
Reference in New Issue
Block a user