fix: download fallback URL when primary fails
- Gitea URL used as primary (same-version tie-break), GitHub as fallback - download() tries base url first; if it fails, automatically retries the fallback URL - version 1.9.1 -> 1.9.2 (code 30 -> 31) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -13,8 +13,8 @@ android {
|
||||
applicationId = "com.a2i.forwarder"
|
||||
minSdk = 34
|
||||
targetSdk = 36
|
||||
versionCode = 29
|
||||
versionName = "1.9.0"
|
||||
versionCode = 30
|
||||
versionName = "1.9.1"
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
|
||||
@@ -26,6 +26,7 @@ data class UpdateInfo(
|
||||
val apkSize: Long,
|
||||
val releaseNotes: String,
|
||||
val releasePageUrl: String,
|
||||
val fallbackUrl: String? = null, // 主 URL 下载失败时尝试的备选链接
|
||||
)
|
||||
|
||||
/**
|
||||
@@ -71,9 +72,12 @@ class UpdateChecker(private val context: Context) {
|
||||
// 先查 GitHub(公开默认源),从其 release notes 动态发现额外源(如自建 Gitea),取最高版本
|
||||
// 同版本时 Gitea 优先(国内可达,GitHub 可能被墙导致下载失败)
|
||||
val infos = mutableListOf<UpdateInfo>()
|
||||
var ghInfo: UpdateInfo? = null
|
||||
var giteaInfo: UpdateInfo? = null
|
||||
val gh = runCatching { fetchRelease(GITHUB_API) }.getOrNull()
|
||||
if (gh != null) {
|
||||
val giteaInfo = parseUpdateSource(gh.releaseNotes)?.let { runCatching { fetchRelease(it) }.getOrNull() }
|
||||
ghInfo = gh
|
||||
giteaInfo = parseUpdateSource(gh.releaseNotes)?.let { runCatching { fetchRelease(it) }.getOrNull() }
|
||||
// Gitea 放前面,同版本时优先用它下载(国内可达)
|
||||
if (giteaInfo != null) infos.add(giteaInfo)
|
||||
infos.add(gh)
|
||||
@@ -92,7 +96,9 @@ class UpdateChecker(private val context: Context) {
|
||||
updateInfo.value = null
|
||||
lastNotifiedVersion = null
|
||||
} else {
|
||||
updateInfo.value = best
|
||||
// 主 URL 下载失败时尝试另一个源的链接
|
||||
val fallback = if (best == giteaInfo) ghInfo?.apkUrl else giteaInfo?.apkUrl
|
||||
updateInfo.value = best.copy(fallbackUrl = fallback)
|
||||
}
|
||||
lastError.value = null
|
||||
|
||||
@@ -179,14 +185,29 @@ class UpdateChecker(private val context: Context) {
|
||||
downloading.value = true
|
||||
downloadProgress.value = 0
|
||||
lastError.value = null
|
||||
try {
|
||||
val req = Request.Builder().url(info.apkUrl).get().build()
|
||||
// 主 URL 失败时自动尝试备选 URL
|
||||
val urls = listOf(info.apkUrl) + (info.fallbackUrl?.let { listOf(it) } ?: emptyList())
|
||||
for ((idx, url) in urls.withIndex()) {
|
||||
if (idx > 0) downloadProgress.value = 0 // 重试时重置进度
|
||||
val file = tryDownload(url, info.latestVersion)
|
||||
if (file != null) {
|
||||
downloading.value = false
|
||||
return@withContext file
|
||||
}
|
||||
}
|
||||
downloading.value = false
|
||||
null
|
||||
}
|
||||
|
||||
private suspend fun tryDownload(url: String, version: String): File? {
|
||||
return try {
|
||||
val req = Request.Builder().url(url).get().build()
|
||||
http.newCall(req).execute().use { res ->
|
||||
if (!res.isSuccessful) { lastError.value = "HTTP ${res.code}"; return@use null }
|
||||
val body = res.body ?: run { lastError.value = "空响应"; return@use null }
|
||||
if (!res.isSuccessful) { lastError.value = "HTTP ${res.code}"; return@tryDownload null }
|
||||
val body = res.body ?: run { lastError.value = "空响应"; return@tryDownload null }
|
||||
val total = body.contentLength()
|
||||
val dir = context.getExternalFilesDir(null) ?: context.cacheDir
|
||||
val target = File(dir, "gotmsg-${info.latestVersion}.apk")
|
||||
val target = File(dir, "gotmsg-${version}.apk")
|
||||
target.outputStream().use { sink ->
|
||||
val input = body.byteStream()
|
||||
val buf = ByteArray(8 * 1024)
|
||||
@@ -205,8 +226,6 @@ class UpdateChecker(private val context: Context) {
|
||||
} catch (e: Exception) {
|
||||
lastError.value = e.message ?: "下载失败"
|
||||
null
|
||||
} finally {
|
||||
downloading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user