下载进度回调改 session-level delegate(ad-hoc 不触发 didWriteData)
LobbyZipUpgrader 和 SubGameDownloader 之前用 `session.download(from:delegate:)` 的 iOS 15+ async API,把 DownloadProgressDelegate 作为 ad-hoc delegate 传入。 该 API 的 delegate 参数实际只收 URLSessionTaskDelegate 回调,**不会**触发 URLSessionDownloadDelegate.didWriteData,所以 0…1 进度从未上报,splash 进度条 一直停在 0,只有末尾兜底的 onProgress(1.0) 跑一下,看起来"一出现就 100%"。 改为每次下载临时新建一个 URLSession,把 progressDelegate 在 init 时绑到 session 级别(带独立 OperationQueue),下载结束 defer invalidateAndCancel 释放。delegate 现在能收到 didWriteData,真实字节比例上报,进度条 0→1 平滑。 副作用:删除 actor 字段 `session` 与可注入的 `init(session:)`,改用静态 `defaultConfig: URLSessionConfiguration` 复用超时配置(无外部调用方使用过自定义 session init)。 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
57d4d93781
commit
fd9976c4b9
@@ -28,18 +28,17 @@ public actor LobbyZipUpgrader {
|
|||||||
case stagingMoveFailed(any Error)
|
case stagingMoveFailed(any Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
private let session: URLSession
|
public init() {}
|
||||||
|
|
||||||
public init(session: URLSession = LobbyZipUpgrader.defaultSession) {
|
/// URLSession 配置:60s 单请求 / 300s 总体(zip 约 11 MB,4G 约 10s)。
|
||||||
self.session = session
|
/// 每次 upgradeIfNeeded 用此 config 临时建 session,绑 download delegate 接收
|
||||||
}
|
/// didWriteData 进度回调;ad-hoc delegate(`session.download(from:delegate:)`)
|
||||||
|
/// 不会触发 URLSessionDownloadDelegate 的 download-specific 方法,必须走 session 级。
|
||||||
/// 默认 URLSession:下载用 60s 超时(zip 大约 11 MB,4G 网络约 10s)。
|
nonisolated private static var defaultConfig: URLSessionConfiguration {
|
||||||
nonisolated public static var defaultSession: URLSession {
|
|
||||||
let cfg = URLSessionConfiguration.default
|
let cfg = URLSessionConfiguration.default
|
||||||
cfg.timeoutIntervalForRequest = 60
|
cfg.timeoutIntervalForRequest = 60
|
||||||
cfg.timeoutIntervalForResource = 300
|
cfg.timeoutIntervalForResource = 300
|
||||||
return URLSession(configuration: cfg)
|
return cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 比较远端 game_version 与本地 version.xml。需要升级则下载 + 解压 + 原子 rename。
|
/// 比较远端 game_version 与本地 version.xml。需要升级则下载 + 解压 + 原子 rename。
|
||||||
@@ -61,15 +60,23 @@ public actor LobbyZipUpgrader {
|
|||||||
return .noop
|
return .noop
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. 下载到 tmp(URLSession 默认管理临时位置),delegate 上报 0…1 进度
|
// 1. 下载到 tmp(URLSession 默认管理临时位置),delegate 上报 0…1 进度。
|
||||||
|
// delegate 必须在 URLSession init 时绑定,否则 didWriteData 不会被调用
|
||||||
|
// (async API `download(from:delegate:)` 的 ad-hoc delegate 只收 task-level 回调)。
|
||||||
let progressDelegate = DownloadProgressDelegate(onProgress: onProgress)
|
let progressDelegate = DownloadProgressDelegate(onProgress: onProgress)
|
||||||
|
let opQueue = OperationQueue()
|
||||||
|
opQueue.maxConcurrentOperationCount = 1
|
||||||
|
let session = URLSession(
|
||||||
|
configuration: Self.defaultConfig,
|
||||||
|
delegate: progressDelegate,
|
||||||
|
delegateQueue: opQueue
|
||||||
|
)
|
||||||
|
defer { session.invalidateAndCancel() }
|
||||||
|
|
||||||
let downloadedURL: URL
|
let downloadedURL: URL
|
||||||
let response: URLResponse
|
let response: URLResponse
|
||||||
do {
|
do {
|
||||||
(downloadedURL, response) = try await session.download(
|
(downloadedURL, response) = try await session.download(from: zipURL)
|
||||||
from: zipURL,
|
|
||||||
delegate: progressDelegate
|
|
||||||
)
|
|
||||||
} catch {
|
} catch {
|
||||||
throw UpgradeError.downloadFailed(error)
|
throw UpgradeError.downloadFailed(error)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,17 +36,17 @@ public actor SubGameDownloader {
|
|||||||
case stagingMoveFailed(any Error)
|
case stagingMoveFailed(any Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
private let session: URLSession
|
public init() {}
|
||||||
|
|
||||||
public init(session: URLSession = SubGameDownloader.defaultSession) {
|
/// URLSession 配置:60s 单请求 / 300s 总体(zip 体量约 1-10 MB)。
|
||||||
self.session = session
|
/// 每次 ensureReady 用此 config 临时建 session,绑 download delegate 接收
|
||||||
}
|
/// didWriteData 进度回调;ad-hoc delegate(`session.download(from:delegate:)`)
|
||||||
|
/// 不会触发 URLSessionDownloadDelegate 的 download-specific 方法,必须走 session 级。
|
||||||
nonisolated public static var defaultSession: URLSession {
|
nonisolated private static var defaultConfig: URLSessionConfiguration {
|
||||||
let cfg = URLSessionConfiguration.default
|
let cfg = URLSessionConfiguration.default
|
||||||
cfg.timeoutIntervalForRequest = 60
|
cfg.timeoutIntervalForRequest = 60
|
||||||
cfg.timeoutIntervalForResource = 300
|
cfg.timeoutIntervalForResource = 300
|
||||||
return URLSession(configuration: cfg)
|
return cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 确保 `{Caches}/{request.gameDir}/{request.gameStart}/index.html` 已就绪。
|
/// 确保 `{Caches}/{request.gameDir}/{request.gameStart}/index.html` 已就绪。
|
||||||
@@ -72,15 +72,23 @@ public actor SubGameDownloader {
|
|||||||
throw DownloadError.badDownloadURL(request.downloadURL)
|
throw DownloadError.badDownloadURL(request.downloadURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. 下载到 tmp
|
// 1. 下载到 tmp。delegate 必须在 URLSession init 时绑定,否则
|
||||||
|
// URLSessionDownloadDelegate.didWriteData 不会被调用(async API
|
||||||
|
// `download(from:delegate:)` 的 ad-hoc delegate 只收 task-level 回调)。
|
||||||
let progressDelegate = DownloadProgressDelegate(onProgress: onProgress)
|
let progressDelegate = DownloadProgressDelegate(onProgress: onProgress)
|
||||||
|
let opQueue = OperationQueue()
|
||||||
|
opQueue.maxConcurrentOperationCount = 1
|
||||||
|
let session = URLSession(
|
||||||
|
configuration: Self.defaultConfig,
|
||||||
|
delegate: progressDelegate,
|
||||||
|
delegateQueue: opQueue
|
||||||
|
)
|
||||||
|
defer { session.invalidateAndCancel() }
|
||||||
|
|
||||||
let downloadedURL: URL
|
let downloadedURL: URL
|
||||||
let response: URLResponse
|
let response: URLResponse
|
||||||
do {
|
do {
|
||||||
(downloadedURL, response) = try await session.download(
|
(downloadedURL, response) = try await session.download(from: zipURL)
|
||||||
from: zipURL,
|
|
||||||
delegate: progressDelegate
|
|
||||||
)
|
|
||||||
} catch {
|
} catch {
|
||||||
throw DownloadError.downloadFailed(error)
|
throw DownloadError.downloadFailed(error)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user