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() // 清理上一个
|
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() {
|
||||||
|
|||||||
Reference in New Issue
Block a user