一、 .NET MAUI网络请求的日常痛点与高效思路
很多用.NET MAUI写跨平台APP的开发者,都会遇到网络请求的糟心事:比如APP加载慢、弱网下经常失败、用户切换页面后请求还在跑、大文件上传时卡顿。这些问题大多不是技术难度高,而是没注意基础细节,做好这些细节就能让网络请求效率翻几倍。
1.1 常见低效问题解析
新手写网络请求时,最容易犯的几个错:每次发请求就new一个HttpClient——这玩意儿创建和销毁都要耗资源,重复新建会占内存还可能导致端口冲突;请求不设超时,用户等半天没反应,以为APP卡了;遇到偶尔的网络闪断,请求直接失败,只能手动重试;用户快速切换页面,旧页面的请求还在跑,浪费流量还占后台资源。
1.2 高效的核心思路
要解决上面的问题,核心就是几个小方向:把HttpClient做成全局共享的实例,只创建一次;给每个请求设合理超时(10秒左右最合适);给临时错误加自动重试(比如网络波动时重试2-3次);用户离开页面就取消未完成的请求;静态数据(比如分类列表)用缓存,不用每次发网络请求。
二、 具体实现步骤与完整示例
下面的代码都用.NET MAUI原生C#栈,所有示例都加了注释,直接复制就能用。
2.1 复用HttpClient的正确姿势
全局共享HttpClient是最基础的优化,在.NET MAUI里用依赖注入实现,不用自己管理实例生命周期:
using Microsoft.Maui;
using Microsoft.Maui.Hosting;
using System.Net.Http;
namespace MauiNetworkDemo
{
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder.UseMauiApp<App>();
// 注册全局HttpClient,设置统一配置
builder.Services.AddSingleton(new HttpClient(new HttpClientHandler())
{
// 设置10秒超时,避免请求无限等待
Timeout = TimeSpan.FromSeconds(10),
// 自定义UA,让服务器识别是.NET MAUI客户端,避免被拦截
DefaultRequestHeaders = { { "User-Agent", ".NET MAUI Demo App" } }
});
return builder.Build();
}
}
}
2.2 带重试机制的请求封装
遇到临时错误(比如502、网络波动)自动重试,用Polly库,先在Nuget装Polly和Polly.Extensions.Http:
using Polly;
using Polly.Extensions.Http;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace MauiNetworkDemo.Services
{
public class ApiService
{
private readonly HttpClient _httpClient;
// 构造函数注入全局HttpClient
public ApiService(HttpClient httpClient)
{
_httpClient = httpClient;
}
// 带重试的GET请求,支持取消令牌
public async Task<string> GetDataWithRetryAsync(string url, CancellationToken ctsToken)
{
// 定义重试策略:最多3次,指数退避(第一次1秒、第二次2秒、第三次4秒)
// 避免短时间发多个请求给服务器,减少压力
var retryPolicy = HttpPolicyExtensions
.HandleTransientHttpError() // 只处理临时错误,比如网络错误、5xx错误,4xx错误不重试
.WaitAndRetryAsync(3, retryAttemp => TimeSpan.FromSeconds(Math.Pow(2, retryAttemp)));
// 执行带重试的请求,传入取消令牌
return await retryPolicy.ExecuteAsync(async () =>
{
var response = await _httpClient.GetAsync(url, ctsToken);
// 如果响应状态码不是成功(比如404、500)直接抛异常,触发重试
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}, ctsToken);
}
}
}
2.3 请求取消与冗余处理
用户切换页面时,取消当前页面的请求,避免浪费资源:
using Microsoft.Maui.Controls;
using System;
using System.Threading;
namespace MauiNetworkDemo.Pages
{
public class ListPage : ContentPage
{
private CancellationTokenSource _cts;
private readonly ApiService _apiService;
public ListPage(ApiService apiService)
{
_apiService = apiService;
LoadListData(); // 页面加载时请求数据
}
private async void LoadListData()
{
_cts = new CancellationTokenSource();
try
{
// 调用带重试的请求,传入取消令牌
var data = await _apiService.GetDataWithRetryAsync("https://api.example.com/goods/list", _cts.Token);
// 把数据显示到列表UI
Console.WriteLine("加载列表数据:" + data);
}
catch (OperationCanceledException)
{
// 请求被取消,不用处理,避免报错
Console.WriteLine("请求已取消,停止加载");
}
catch (Exception ex)
{
// 处理其他错误,比如网络完全不通
Console.WriteLine("请求失败:" + ex.Message);
}
}
// 页面消失时(比如跳转到详情页),取消正在进行的请求
protected override void OnDisappearing()
{
base.OnDisappearing();
_cts?.Cancel();
_cts?.Dispose();
_cts = null;
}
}
}
三、 不同场景下的高效实践
3.1 列表类接口的请求优化
列表是APP最常见的接口,比如商品列表、消息列表,优化点:用分页加载(每次拉20条),拉到最后一页就停止;重复请求的页用缓存,比如拉过第1页后,下次再进列表直接取缓存,后台悄悄更新新数据。
3.2 文件上传下载的高效处理
传大文件(比如头像、视频)时,别把整个文件转成字节数组,用流式传输,减少内存占用:
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace MauiNetworkDemo.Services
{
public partial class ApiService
{
// 流式上传文件,避免占大量内存
public async Task<bool> UploadImageAsync(System.IO.Stream imageStream, string fileName, CancellationToken ctsToken)
{
using var content = new MultipartFormDataContent();
// 直接用文件流,不用加载整个文件到内存
var streamContent = new StreamContent(imageStream);
streamContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("image/jpeg");
content.Add(streamContent, "file", fileName);
var response = await _httpClient.PostAsync("https://api.example.com/upload", content, ctsToken);
return response.IsSuccessStatusCode;
}
}
}
3.3 离线场景的缓存策略
弱网或者无网时,APP能显示已加载的内容,不用报错,用MAUI的Preferences存简单数据(适合小容量数据),IFileSystem.CacheDirectory存文件:
using Microsoft.Maui.Storage;
// 缓存静态数据,比如商品分类
public async Task CacheStaticDataAsync(string key, string data)
{
await Preferences.SetAsync(key, data);
}
// 获取缓存数据,返回null就是无缓存
public async Task<string> GetCachedDataAsync(string key)
{
return await Preferences.GetAsync(key, null);
}
四、 技术选型与注意事项
4.1 技术优缺点分析
用原生HttpClient的优点是不用额外依赖,轻量,适合简单请求;缺点是需要自己写重试、超时等逻辑,灵活性差点。用Polly的优点是容错策略灵活,支持重试、熔断、降级等,缺点是要加Nuget包,不过体积可以忽略。
4.2 关键注意事项
别在主线程写网络请求,必须用async/await,否则UI会卡住;iOS要加ATS例外,在Info.plist里允许你的API域名,不然iOS会拦截HTTP请求;Android要加ACCESS_NETWORK_STATE权限(如果需要判断网络状态);请求头别乱加,服务器可能会过滤异常头。
五、 总结
.NET MAUI做网络请求的高效性,全在细节:复用HttpClient减少资源消耗,设超时避免假死,加重试处理临时问题,用取消令牌处理冗余请求,缓存静态数据减少流量。这些小技巧不需要复杂技术,只要做好,就能让APP在弱网、多场景下都流畅,给用户好体验。
Comments