From cacbdb6aaa0f62267f433cfbcf2a80b7450153cf Mon Sep 17 00:00:00 2001 From: dongliang Date: Thu, 7 May 2026 12:23:54 +0930 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=9B=BE=E7=89=87=E5=92=8C=E8=AF=AD?= =?UTF-8?q?=E9=9F=B3=E6=94=B9=E7=94=A8=20HttpURLConnection=20=E4=B8=8B?= =?UTF-8?q?=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OkHttp 在该设备连 OSS 的 HTTPS/HTTP 都被 reset。 改用系统 HttpURLConnection(走系统 TLS 栈,可能支持不同 cipher suite)。 图片和语音统一使用原始 HTTPS URL。 Co-Authored-By: Claude Opus 4.6 (1M context) --- .../xiaoqu/watch/ui/task/TaskListFragment.kt | 44 +++++++++---------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/com/xiaoqu/watch/ui/task/TaskListFragment.kt b/app/src/main/java/com/xiaoqu/watch/ui/task/TaskListFragment.kt index a377aac..22ff00c 100644 --- a/app/src/main/java/com/xiaoqu/watch/ui/task/TaskListFragment.kt +++ b/app/src/main/java/com/xiaoqu/watch/ui/task/TaskListFragment.kt @@ -19,8 +19,6 @@ import com.xiaoqu.watch.databinding.FragmentTaskListBinding import com.xiaoqu.watch.network.ApiResult import com.xiaoqu.watch.network.api.TaskApi import com.xiaoqu.watch.network.safeApiCall -import okhttp3.OkHttpClient -import okhttp3.Request import com.xiaoqu.watch.service.manager.NfcTaskManager import com.xiaoqu.watch.ui.common.BaseFragment import com.xiaoqu.watch.ui.widget.QuTipDialog @@ -45,8 +43,6 @@ class TaskListFragment : BaseFragment() { @Inject lateinit var taskApi: TaskApi @Inject lateinit var nfcTaskManager: NfcTaskManager - /** 下载用的 OkHttp(不带签名拦截器,走 HTTP 避免 TLS 问题) */ - private val downloadClient by lazy { OkHttpClient.Builder().build() } /** 任务 ID 列表(queryTaskIds 返回) */ private var taskList: List = emptyList() @@ -438,10 +434,9 @@ class TaskListFragment : BaseFragment() { scaleType = android.widget.ImageView.ScaleType.CENTER_CROP setBackgroundColor(requireContext().getColor(R.color.card_background)) } - val httpUrl = pic.url.replace("https://", "http://") - loadImage(imageView, httpUrl) + loadImage(imageView, pic.url) // 点击放大全屏查看 - imageView.setOnClickListener { showFullImage(httpUrl) } + imageView.setOnClickListener { showFullImage(pic.url) } binding.picContainer.addView(imageView) } } @@ -471,18 +466,21 @@ class TaskListFragment : BaseFragment() { loadImage(imageView, url) } - /** 异步加载图片到 ImageView */ + /** 异步加载图片到 ImageView(使用系统 HttpURLConnection) */ private fun loadImage(imageView: android.widget.ImageView, url: String) { viewLifecycleOwner.lifecycleScope.launch { try { val bitmap = kotlinx.coroutines.withContext(kotlinx.coroutines.Dispatchers.IO) { - val request = Request.Builder().url(url).build() - val response = downloadClient.newCall(request).execute() - if (response.isSuccessful) { - response.body?.byteStream()?.let { - android.graphics.BitmapFactory.decodeStream(it) - } - } else null + val conn = java.net.URL(url).openConnection() as java.net.HttpURLConnection + conn.connectTimeout = 10000 + conn.readTimeout = 10000 + conn.connect() + if (conn.responseCode == 200) { + conn.inputStream.use { android.graphics.BitmapFactory.decodeStream(it) } + } else { + Timber.w("图片HTTP错误: ${conn.responseCode} $url") + null + } } bitmap?.let { imageView.setImageBitmap(it) } } catch (e: Exception) { @@ -549,21 +547,21 @@ class TaskListFragment : BaseFragment() { } } - /** 用 OkHttp 下载音频文件到缓存目录 */ + /** 下载音频文件到缓存目录(使用系统 HttpURLConnection) */ private fun downloadToCache(url: String): java.io.File? { return try { - // OSS 的 HTTPS 在 Android 8.1 设备 TLS 握手失败,降级为 HTTP - val httpUrl = url.replace("https://", "http://") - val request = Request.Builder().url(httpUrl).build() - val response = downloadClient.newCall(request).execute() - if (!response.isSuccessful) { - Timber.w("语音下载HTTP错误: ${response.code}") + val conn = java.net.URL(url).openConnection() as java.net.HttpURLConnection + conn.connectTimeout = 15000 + conn.readTimeout = 15000 + conn.connect() + if (conn.responseCode != 200) { + Timber.w("语音下载HTTP错误: ${conn.responseCode}") return null } // 写入缓存文件 val fileName = "voice_${url.hashCode()}.mp3" val cacheFile = java.io.File(requireContext().cacheDir, fileName) - response.body?.byteStream()?.use { input -> + conn.inputStream.use { input -> cacheFile.outputStream().use { output -> input.copyTo(output) }