Xamarin Android下载重试第二次后停滞问题求助
我之前在Xamarin Android开发中也碰到过类似的重试中断问题,咱们来一步步排查和解决:
首先,你的Polly异常捕获范围不全!
这是最可能导致第二次重试失败的核心原因。你当前只捕获了NetworkOnMainThreadException、Java.Net.UnknownHostException和SSLException,但实际网络中断时,下载流程可能抛出其他未被覆盖的异常——比如流读取失败的IOException、请求超时的TaskCanceledException、HttpClient通用请求错误的HttpRequestException。这些异常没被Polly捕获的话,一旦抛出就会直接终止重试流程。
修改你的Polly配置,把这些异常加上,同时添加日志排查每次的异常类型:
await Policy .Handle<NetworkOnMainThreadException>() .Or<Java.Net.UnknownHostException>() .Or<SSLException>() .Or<IOException>() .Or<TaskCanceledException>() .Or<HttpRequestException>() .WaitAndRetryAsync( new[] { TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(10) }, (exception, timeSpan, retryCount, context) => { // 打印日志,确认每次重试的异常类型,方便排查 Console.WriteLine($"第{retryCount}次重试,等待{timeSpan.TotalSeconds}秒,异常原因:{exception.Message}"); }) .ExecuteAsync(async () => { totalRead = await DownloadFile(url, progress, totalRead, token); });
其次,检查Android 9.0的网络限制
Android 9.0(API 28)默认禁用了明文HTTP请求,如果你的下载URL是http://而不是https://,第一次请求可能因为这个直接失败,重试也会无效。解决方法:
在AndroidManifest.xml的<application>标签里添加:
android:usesCleartextTraffic="true"
如果是HTTPS请求,可能存在SSL会话缓存问题,可以初始化HttpClient时禁用缓存:
var handler = new Xamarin.Android.Net.AndroidMessageHandler(); handler.DisableCaching = true; handler.SSLProtocols = System.Security.Authentication.SslProtocols.Tls12; // 生产环境请不要跳过证书验证,这里仅为测试场景提供 handler.ServerCertificateCustomValidationCallback = (msg, cert, chain, errors) => true; _client = new HttpClient(handler);
另外,重试时的断点续传逻辑需要完善
你当前的代码是基于totalRead累加来追加文件,但如果服务器不支持断点续传,重试时会从头下载,导致文件内容重复。建议在DownloadFile里先检查服务器是否支持Range请求:
private async Task<long> DownloadFile(string url, IProgress<double> progress, long totalRead, CancellationToken token) { // 先检查服务器是否支持断点续传 var headRequest = new HttpRequestMessage(HttpMethod.Head, url); var headResponse = await _client.SendAsync(headRequest, token); bool supportsRange = headResponse.Headers.AcceptRanges.Contains("bytes"); // 如果不支持断点续传,重置已下载进度并删除现有文件 if (!supportsRange && totalRead > 0) { totalRead = 0; var fileName = url.Split('/').Last(); var filePath = Path.Combine(_fileService.GetStorageFolderPath(), fileName); if (File.Exists(filePath)) File.Delete(filePath); } // 构建下载请求,支持断点续传 var downloadRequest = new HttpRequestMessage(HttpMethod.Get, url); if (totalRead > 0 && supportsRange) { downloadRequest.Headers.Range = new System.Net.Http.Headers.RangeHeaderValue(totalRead, null); } var response = await _client.SendAsync(downloadRequest, HttpCompletionOption.ResponseHeadersRead, token); if (!response.IsSuccessStatusCode) { if (response.StatusCode == System.Net.HttpStatusCode.RequestedRangeNotSatisfiable) { // 已下载完成,直接返回当前进度 return totalRead; } throw new Exception($"请求失败,HTTP状态码:{response.StatusCode}"); } var fileName = url.Split('/').Last(); var buffer = new byte[bufferSize]; var totalData = response.Content.Headers.ContentLength.GetValueOrDefault(-1L); // 断点续传时,总数据量是已下载部分加上本次响应的长度 if (supportsRange && totalRead > 0) totalData += totalRead; var filePath = Path.Combine(_fileService.GetStorageFolderPath(), fileName); using (var fileStream = OpenStream(filePath, totalRead)) using (var inputStream = await response.Content.ReadAsStreamAsync()) { int bytesRead; while ((bytesRead = await inputStream.ReadAsync(buffer, 0, buffer.Length, token)) > 0) { totalRead += bytesRead; await fileStream.WriteAsync(buffer, 0, bytesRead, token); progress.Report(totalData > 0 ? (totalRead * 100d) / totalData : 0); } progress.Report(100); // 下载完成后报告100%,而非0 } return totalRead; }
最后,排查CancellationToken是否被意外取消
检查你的token是否在重试过程中被触发了取消操作(比如用户点击了取消按钮),如果token已取消,Polly会直接终止重试流程。
按上面的步骤调整后,重试流程应该就能正常执行了。
内容的提问来源于stack exchange,提问作者Henkie85




