GeneralUpdate.Maui.Android — 执行流程详解
目标读者: 需要在 .NET MAUI Android 应用中集成自动更新的开发者
阅读完你将理解:
AndroidBootstrap的合并式两步 API 设计意图(ValidateAsync → ExecuteUpdateAsync)HttpRangeDownloader的可恢复下载机制:Range 请求 + 临时文件 + 原子替换ExecuteUpdateAsync内部的 Interlocked 并发保护与状态原子转换- SHA256 校验失败后自动清理损坏文件的策略
- Android Package Installer 的 FileProvider + Intent 触发流程
AddGeneralUpdateMauiAndroid()DI 注册扩展的设计- 与 Avalonia.Android 在 API 风格和 DI 策略上的核心差异
- 异常到
UpdateFailureReason的分类映射
目录
- 架构总览
- 入口:DI 优先的工厂设计
- AndroidBootstrap:合并式两步 API
- Step 1:ValidateAsync — 版本校验
- Step 2:ExecuteUpdateAsync — 原子执行完整更新
- 可恢复下载:HttpRangeDownloader 深度解析
- SHA256 校验与损坏文件清理
- APK 安装:平台守卫的 Installer
- 并发安全:Interlocked 原子操作
- SafeInvoke:防御式事件触发
- 异常映射:MapFailureReason 分类机制
- 与 Avalonia.Android 的设计对比
- 关键代码路径索引
1. 架构总览
1.1 五服务 DI 优先架构
Maui.Android 采用DI 优先 + 手动装配并存的设计:
┌──────────────────────────────────────────────────────────────┐
│ GeneralUpdateBootstrap(静态 工厂) │
│ CreateDefault() → IAndroidBootstrap │
│ AddGeneralUpdateMauiAndroid(services) → IServiceCollection │
├──────────────────────────────────────────────────────────────┤
│ AndroidBootstrap(编排层) │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │
│ │ IUpdate │ │ IHash │ │ IApkInstaller │ │
│ │ Downloader │ │ Validator │ │ FileProvider │ │
│ │ HTTP 可恢复 │ │ SHA256 校验 │ │ Intent 触发 │ │
│ └──────────────┘ └──────────────┘ └──────────────────┘ │
│ │
│ ┌──────────────┐ ┌──────────────────────────────────────┐ │
│ │ IUpdate │ │ HttpDownloadOptions │ │
│ │ Storage │ │ SSL / 代理 / 超时 / 重试 / 认证 │ │
│ │ Provider │ └──────────────────────────────────────┘ │
│ │ 路径+原子替换 │ │
│ └──────────────┘ │
└──────────────────────────────────────────────────────────────┘
1.2 与 Avalonia.Android 的核心差异
| 维度 | Maui.Android | Avalonia.Android |
|---|---|---|
| API 风格 | 两步合并式(Validate + ExecuteUpdate) | 三步显式(Validate + DownloadAndVerify + LaunchInstaller) |
| DI 策略 | DI 优先,AddGeneralUpdateMauiAndroid() | 手动装配优先,CreateDefault() |
| 并发保护 | Interlocked 原子操作 + 状态原子转换 | SemaphoreSlim(1,1) 操作门 |
| 事件安全 | SafeInvoke 迭代委托列表 | IUpdateEventDispatcher 调度 |
| 平台守卫 | #if ANDROID 编译时守卫 | 运行时平台检查 |
| 下载临时文件 | .downloading 扩展名 | .part + .json sidecar |
| 进度报告 | IProgress<DownloadStatistics> | EventHandler<DownloadProgressChangedEventArgs> |
| 完成阶段 | 4 阶段事件(Download/Verify/Install/Workflow) | 2 事件(Completed/Failed) |
2. 入口:DI 优先的工厂设计
2.1 DI 注册扩展
public static class GeneralUpdateBootstrap
{
// DI 优先:一键注册所有服务
public static IServiceCollection AddGeneralUpdateMauiAndroid(
this IServiceCollection services,
HttpClient? httpClient = null)
{
services.AddSingleton<IUpdateDownloader>(sp =>
new HttpRangeDownloader(httpClient ?? new HttpClient()));
services.AddSingleton<IHashValidator, Sha256Validator>();
services.AddSingleton<IApkInstaller, AndroidApkInstaller>();
services.AddSingleton<IUpdateStorageProvider, UpdateFileStore>();
services.AddSingleton<IUpdateLogger, DefaultUpdateLogger>();
services.AddSingleton<IAndroidBootstrap, AndroidBootstrap>();
return services;
}
// 手动装配:非 DI 场景
public static IAndroidBootstrap CreateDefault(
HttpClient? httpClient = null,
IUpdateLogger? logger = null,
HttpDownloadOptions? httpOptions = null)
{
var client = httpClient ?? new HttpClient();
return new AndroidBootstrap(
new HttpRangeDownloader(client),
new Sha256Validator(),
new AndroidApkInstaller(),
new UpdateFileStore(),
logger);
}
}
2.2 MAUI 应用中的典型注册
// MauiProgram.cs
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder.Services.AddGeneralUpdateMauiAndroid();
// ...
return builder.Build();
}
// 使用
public class UpdateService
{
private readonly IAndroidBootstrap _bootstrap;
public UpdateService(IAndroidBootstrap bootstrap)
{
_bootstrap = bootstrap;
}
}
3. AndroidBootstrap:合并式两步 API
3.1 完整生命周期
3.2 状态机
None → Checking → UpdateAvailable → Downloading → Verifying → ReadyToInstall → Installing → Completed
↓ ↓
Failed/ Failed/
Canceled Canceled
状态通过 Interlocked.Exchange 原子转换:
public UpdateState CurrentState => (UpdateState)Volatile.Read(ref _currentState);
private void ChangeState(UpdateState state)
{
Interlocked.Exchange(ref _currentState, (int)state);
}
4. Step 1:ValidateAsync — 版本校验
4.1 输入验证
private static void ValidateInputs(UpdatePackageInfo packageInfo, UpdateOptions options)
{
ArgumentNullException.ThrowIfNull(packageInfo);
ArgumentNullException.ThrowIfNull(options);
if (string.IsNullOrWhiteSpace(options.CurrentVersion))
throw new ArgumentException("Current version cannot be null or empty.");
if (string.IsNullOrWhiteSpace(packageInfo.Version))
throw new ArgumentException("Update package version cannot be null or empty.");
if (string.IsNullOrWhiteSpace(packageInfo.DownloadUrl))
throw new ArgumentException("Update package download url cannot be null or empty.");
if (string.IsNullOrWhiteSpace(packageInfo.Sha256))
throw new ArgumentException("Update package SHA256 cannot be null or empty.");
}
安全设计: SHA256 为必填项——Maui.Android 不允许跳过完整性校验。
5. Step 2:ExecuteUpdateAsync — 原子执行完整更新
这是 Maui.Android 最核心的方法。它将下载、校验、安装合并在一个原子操作中。
5.1 全流程总图
5.2 四个完成阶段
| 阶段 | UpdateCompletionStage | 触发时机 |
|---|---|---|
| 下载完成 | DownloadCompleted | 文件下载完成 + 原子替换后 |
| 校验完成 | VerificationCompleted | SHA256 校验通过后 |
| 安装触发 | InstallationTriggered | Android Installer Intent 发出后 |
| 流程完成 | WorkflowCompleted | 所有步骤成功, 状态设为 Completed |
6. 可恢复下载:HttpRangeDownloader 深度解析
6.1 下载流程
HttpRangeDownloader.DownloadAsync()
│
├── HEAD 请求探测
│ Content-Length, Accept-Ranges, ETag
│
├── 检查已有部分下载
│ 临时文件路径:{targetFilePath}.downloading
│
├── 续传判断
│ 如果临时文件存在:
│ → 文件大小 ≤ Content-Length → Range: bytes={size}-
│ → 否则删除临时文件,从头下载
│
├── GET with Range
│ 流式写入临时文件(追加模式)
│ 实时报告进度:字节数、总大小、速度
│
└── 进度报告
通过 IProgress<DownloadStatistics> 回调
6.2 DownloadStatistics
public class DownloadStatistics
{
public long BytesDownloaded { get; set; }
public long? TotalBytes { get; set; }
public double DownloadSpeedBytesPerSecond { get; set; }
public int ProgressPercentage { get; set; }
public TimeSpan? EstimatedTimeRemaining { get; set; }
}