GeneralUpdate.Avalonia.Android
命名空间: GeneralUpdate.Avalonia.Android | 主要入口: GeneralUpdateBootstrap.CreateDefault() | NuGet 包: GeneralUpdate.Avalonia.Android
1. 组件简介
1.1 组件概述
GeneralUpdate.Avalonia.Android 是面向 Avalonia 12+ 应用的 Android 平台自动更新核心库(无 UI,net10.0-android),专为 Android APK 的更新场景设计。它提供版本查询与对比、可断点续传的 APK 下载、SHA256 完整性校验、Android Package Installer 拉起,以及安装结果闭环核对的全流程编排能力。
Android 平台的更新与桌面应用不同:APK 安装必须通过 Android 系统安装器完成,下载过程可能被用户中断或切换网络,安装前需要检查"安装未知应用"权限,而且安装期间当前进程会被系统结束。该组件封装了这些平台差异,并额外解决了"安装器已拉起 ≠ 安装成功"的确认问题。
核心能力:
| 能力 | 说明 |
|---|---|
| 服务端版本查询 | 组件按 UpdateServerOptions 内部请求服务端,调用方只需提供设备上的当前版本 |
| 版本对比 | 内置 SystemVersionComparer,通过 IVersionComparer 接口支持自定义版本策略 |
| 一体化流程 | PrepareUpdateAsync 在同一个操作锁内完成查询 → 预检查 → 下载 → 校验 |
| 断点续传下载 | HttpResumableApkDownloader 基于 HTTP Range + Sidecar 元数据,中断后可续传 |
| SHA256 完整性校验 | 下载完成后自动计算 SHA256,与服务端声明的哈希比对,并校验文件大小 |
| APK 安装编排 | 通过 AndroidApkInstaller 调用系统 Package Installer,自动处理 FileProvider URI |
| 安装结果闭环 | CheckInstallationAsync / ResetInstallationAsync 依据持久化记录核对本次更新是否真正生效 |
| 更新前回调 | AddListenerUpdatePrecheck 在下载前把更新信息交给业务层,可跳过本次更新 |
| HTTP 传输配置 | HttpDownloadOptions 支持 SSL/TLS 证书策略、代理、超时、重试和认证 |
| 多协议认证 | HMAC-SHA256、Bearer Token、API Key、HTTP Basic,可全局或单包粒度配置 |
| 事件通知 | 验证、下载进度、完成、安装确认、失败五类事件 |
| 状态快照 | GetSnapshot() 随时获取当前更新状态、失败原因和消息 |
| UI 线程调度 | IUpdateEventDispatcher 支持把事件调度到 UI 线程(如 Avalonia 的 Dispatcher.UIThread) |
| 操作串行化 | 内部 SemaphoreSlim(1,1) 操作门 ,并发调用自动排队,线程安全 |
| 可替换抽象 | 版本比较、下载、哈希、安装、存储、包源、事件调度、日志全部可替换 |
解决的业务痛点:
- Avalonia 应用发布 Android 版本后,需要可靠的内置更新能力,但 Android APK 安装必须经由系统安装器
- 调用方不应重复实现"查询版本接口 + 挑选最新包 + 比较版本"的样板代码
- 下载大 APK 时可能被中断或切换网络,需要断点续传降低流量消耗
- 需要校验下载包的完整性,防止传输损坏或被篡改
- 安装器返回
Success并不代表用户完成了安装,需要跨进程的安装结果确认机制 - 需要灵活的认证方案对接不同服务端的鉴权要求
Android 平台不能像桌面端那样直接替换文件。APK 必须通过 Android Package Installer 安装,系统会处理签名验证、权限授予和应用替换。本组件封装了这些平台差异,但 APK 签名、包名一致、versionCode 递增等仍需要你在构建流水线中保证。组件也不提供静默安装、自动重启或回滚。
业务使用场景:
- Avalonia 跨平台应用的 Android APK 自动更新
- 企业内部 Android 应用的分阶段、可确认式更新
- 需要通过 CDN / OSS 分发 APK 包的场景
- 需要对接 GeneralUpdate 示例服务端
/Upgrade/Verification协议或静态 JSON 端点的场景
1.2 环境与依赖
| 项目 | 说明 |
|---|---|
| NuGet 包 | GeneralUpdate.Avalonia.Android(仓库当前版本 v0.0.1-beta.10) |
| 目标框架 | net10.0-android(SupportedOSPlatformVersion = 26.0) |
| .NET SDK | 10.0+ |
| Avalonia | 12+ |
| 依赖包 | Xamarin.AndroidX.Core(FileProvider 支持) |
| 兼容性 | Android 8.0 (API 26) 及以上版本 |
2. 组件功能列表
| 功能名称 | 功能描述 | 类型 | 是否必填 | 备注限制 |
|---|---|---|---|---|
| 服务端版本查询 | 按 UpdateServerOptions 请求服务端,选中最新可用 APK | 基础 | 必选 | ValidateAsync(currentVersion);也可注入 IUpdatePackageSource 替代 |
| 版本对比检查 | 比对当前版本与目标版本,判断是否有更新可用 | 基础 | 必选 | 支持自定义 IVersionComparer |
| 一体化更新准备 | 一次调用完成查询、预检查、下载与校验 | 拓展 | 可选 | PrepareUpdateAsync(currentVersion) |
| APK 断点续传下载 | 从服务端下载 APK 包,支持 HTTP Range 断点续传 | 基础 | 必选 | DownloadAndVerifyAsync(packageInfo) |
| SHA256 校验 | 下载完成后自动计算 SHA256 与服务端哈希比对 | 基础 | 自动 | 通过 IHashValidator 实现 |
| 文件大小校验 | FileSize > 0 时校验实际文件长度 | 基础 | 自动 | 不匹配返回 FileIoError 并删除文件 |
| APK 安装触发 | 调用 Android Package Installer 安装 APK | 基础 | 必选 | LaunchInstallerAsync(packageInfo, apkFilePath) |
| 安装结果确认 | 依据持久化安装记录核对本机实际版本 | 基础 | 推荐 | CheckInstallationAsync(currentVersion) |
| 安装记录重置 | 显式清除升级记录(含损坏数据) | 拓展 | 可选 | ResetInstallationAsync() |
| 更新前回调 | 下载前拦截更新,可跳过本次更新 | 拓展 | 可选 | AddListenerUpdatePrecheck |
| HTTP 传输配置 | SSL 证书校验、代理、超时、重试、认证 | 拓展 | 可选 | HttpDownloadOptions |
| 多协议认证 | HMAC-SHA256 / Bearer / API Key / Basic | 拓展 | 可选 | 全局或单包粒度配置 |
| 进度通知 | 下载进度(速度、字节数、百分比、状态描述) | 基础 | 可选 | AddListenerDownloadProgressChanged |
| 完成通知 | 下载校验完成 / 安装器拉起完成 | 基础 | 可选 | AddListenerUpdateCompleted |
| 安装确认通知 | 首次持久化确认安装成功时触发 | 基础 | 可选 | AddListenerInstallationConfirmed |
| 失败通知 | 失败原因、异常信息、失败包信息 | 基础 | 可选 | AddListenerUpdateFailed |
| 验证通知 | 发现可用更新时触发 | 基础 | 可选 | AddListenerValidate |
| 状态快照 | 随时获取当前更新状态 | 拓展 | 可选 | GetSnapshot() |
| UI 线程调度 | 自定义事件调度策略 | 拓展 | 可选 | 实现 IUpdateEventDispatcher |
| 自定义包源 | 自定义版本查询协议 | 拓展 | 可选 | 实现 IUpdatePackageSource |
| 自定义下载器 | 自定义下载实现 | 拓展 | 可选 | 实现 IUpdateDownloader |
| 自定义哈希验证 | 自定义哈希算法 | 拓展 | 可选 | 实现 IHashValidator |
| 自定义版本比较 | 自定义版本对比逻辑 | 拓展 | 可选 | 实现 IVersionComparer |
| 自定义 APK 安装器 | 自定义安装实现 | 拓展 | 可选 | 实现 IApkInstaller |
| 自定义文件存储 | 自定义文件读写实现 | 拓展 | 可选 | 实现 IFileStorage |
| 自定义安装记录存储 | 自定义升级记录持久化 | 拓展 | 可选 | 实现 IInstallationStore |
| 自定义 SSL 策略 | 自定义 HTTPS 证书校验 | 拓展 | 可选 | 实现 ISslValidationPolicy |
| 自定义 HTTP 认证 | 自定义 HTTP 请求认证 | 拓展 | 可选 | 实现 IHttpAuthProvider |
3. API 配置说明
3.1 配置字段(属性 Props)
AndroidUpdateOptions:
| 字段名 | 数据类型 | 默认值 | 是否必填 | 说明 |
|---|---|---|---|---|
UpdateServer | UpdateServerOptions? | null | 条件必填 | ValidateAsync 查询的服务端配置;为 null 且未注入自定义包源时,验证以 InvalidMetadata 失败 |
DownloadDirectoryPath | string | ""(自动使用 <CacheDir>/update 或 TempPath/update) | 可选 | 下载文件存放目录,为空时自动选择 |
InstallationStateFilePath | string | ""(自动使用 <FilesDir>/update/installation.json) | 可选 | 安装记录持久化路径。不要放在下载缓存目录,Android 可能清理缓存 |
TemporaryFileExtension | string | ".part" | 可选 | 临时下载文件扩展名 |
SidecarExtension | string | ".json" | 可选 | 断点续传元数据文件扩展名 |
FileProviderAuthority | string | "" | 条件必填 | Android FileProvider authority,必须与 AndroidManifest 一致;调用安装器时为空返回 InvalidMetadata |
AllowInsecureHttpDownloads | bool | false | 可选 | 允许以明文 HTTP 下载 APK,仅限可信开发环境;即使开启,全局认证也不会通过 HTTP 发送 |
Language | UpdateLanguage | English | 可选 | 内建提示消息语言(English / Chinese) |
DownloadBufferSize | int | 65536(64 KB) | 可选 | 下载缓冲区大小(字节),必须大于 0 |
SpeedSmoothingWindowSeconds | int | 4 | 可选 | 下载速度平滑窗口(秒) |
UpdateServerOptions:
| 字段名 | 数据类型 | 默认值 | 是否必填 | 说明 |
|---|---|---|---|---|
RequestUrl | string | — | 是 | 绝对 HTTP(S) 地址。默认协议下是版本验证端点(如 https://example.com/Upgrade/Verification) |
AppKey | string | "" | 可选 | 验证请求体中的应用标识(仅请求字段,不启用 HMAC 签名) |
AppType | int | 1 | 可选 | 验证请求体中的应用类型 |
Platform | int | 0 | 条件必填 | 服务端配置的 Android 平台编号,必须与实际部署一致 |
ProductId | string | "" | 可选 | 验证请求体中的产品标识 |
UseJsonEndpoint | bool | false | 可选 | 改为 GET 单个 UpdatePackageInfo JSON,而不是 POST 验证协议 |
HttpDownloadOptions:
| 字段名 | 数据类型 | 默认值 | 是否必填 | 说明 |
|---|---|---|---|---|
SslValidationPolicy | ISslValidationPolicy? | null | 可选 | 自定义 SSL 证书验证策略;null 使用系统默认。使用外部 HttpClient 时必须配置在其 handler 上 |
RequestTimeout | TimeSpan | 30s | 可选 | 验证请求与下载 HEAD 探测的超时 |
DownloadTimeout | TimeSpan | 10min | 可选 | 整个下载操作(含重试等待)的超时 |
Proxy | IWebProxy? | null | 可选 | HTTP 代理,需同时设置 UseProxy = true |
UseProxy | bool | false | 可选 | 是否启用配置的代理 |
MaxRetryAttempts | int | 3 | 可选 | 瞬态 HEAD/GET/响应流失败的最大下载尝试次数(3 = 首次 1 次 + 重试 2 次);不作用于元数据查询 |
RetryBaseDelay | TimeSpan | 1s | 可选 | 指数退避基本延迟:min(30s, baseDelay * 2^attempt) |
AuthProvider | IHttpAuthProvider? | null | 可选 | 全局认证提供器;仅发送给与验证端点同源的 HTTPS 下载 地址,单包认证优先级更高 |
UpdatePackageInfo:
| 字段名 | 数据类型 | 默认值 | 是否必填 | 说明 |
|---|---|---|---|---|
Version | string | — | 是 | 目标版本号 |
DownloadUrl | string | — | 是 | APK 下载地址(默认要求 HTTPS) |
Sha256 | string | — | 是 | 64 位十六进制 SHA256,用于完整性校验 |
FileSize | long | 0 | 可选 | APK 文件大小(字节),0 表示未知;已知时参与校验 |
FileName | string? | null | 可选 | 下载后的文件名 |
IsForced | bool | false | 可选 | 是否强制更新;为 true 时不会调用 pre-check 回调 |
VersionName | string? | null | 可选 | 版本展示名称 |
Description | string? | null | 可选 | 版本描述 / 更新日志 |
PublishTime | DateTimeOffset? | null | 可选 | 发布时间 |
AuthScheme | AuthScheme? | null | 可选 | 单包认证方案,优先于全局配置 |
AuthToken | string? | null | 可选 | Bearer 或 API Key 认证令牌 |
AuthSecretKey | string? | null | 可选 | HMAC-SHA256 签名密钥 |
BasicUsername | string? | null | 可选 | HTTP Basic 用户名 |
BasicPassword | string? | null | 可选 | HTTP Basic 密码 |
UpdateState 枚举:
| 枚举值 | 数值 | 说明 |
|---|---|---|
None | 0 | 初始状态 / 无安装记录 |
Checking | 1 | 正在检查更新 |
UpdateAvailable | 2 | 发现可用更新 |
Downloading | 3 | 正在下载 |
Verifying | 4 | 正在校验 |
ReadyToInstall | 5 | 已下载并校验完成,可安装 |
Installing | 6 | 安装器已拉起 |
Completed | 7 | 流程完成(无更新或被 pre-check 跳过也为此状态) |
Failed | 8 | 更新失败 |
Canceled | 9 | 已取消 |
InstallationPending | 10 | 有安装记录但尚未确认安装成功 |
Installed | 11 | 本机版本已达到或超过安装目标 |
UpdateFailureReason 枚举:
| 枚举值 | 数值 | 说明 |
|---|---|---|
None | 0 | 无失败 |
NetworkError | 1 | 网络错误或超时 |
Canceled | 2 | 主动取消 |
InvalidMetadata | 3 | 元数据无效(缺少 URL/SHA256、未配置包源或 FileProvider authority 等) |
FileIoError | 4 | 文件读写错误(含文件大小不匹配、安装记录写入失败) |
HashMismatch | 5 | SHA256 校验失败 |
ServerDoesNotSupportRange | 6 | 服务端不支持 Range 续传 |
InstallPermissionDenied | 7 | 缺少"安装未知应用"权限 |
InstallLaunchFailed | 8 | 安装器 Intent 拉起失败 |
VersionComparisonFailed | 9 | 版本比较失败 |
Unknown | 10 | 未知原因 |
AuthScheme 枚举:
| 枚举值 | 说明 |
|---|---|
Hmac | HMAC-SHA256 签名认证(X-Update-Timestamp + X-Update-Signature) |
Bearer | Bearer Token 认证 |
ApiKey | API Key 认证(默认 header X-Api-Key) |
Basic | HTTP Basic 认证 |
UpdateLanguage 枚举:
| 枚举值 | 说明 |
|---|---|
English | 内建提示消息使用英文(默认) |
Chinese | 内建提示消息使用简体中文 |
3.2 结果模型继承层次
UpdateOperationResult(基类)
├── Success, State, FailureReason, Message, PackageInfo, FilePath, Exception
├── UpdateCheckResult → + UpdateFound, CurrentVersion, TargetVersion
├── UpdatePreparationResult → + UpdateFound, IsReadyToInstall
├── InstallationCheckResult → + CurrentVersion, Record, IsInstalled, HasPendingInstallation
├── DownloadResult
├── HashValidationResult → + ActualSha256, ExpectedSha256
└── InstallResult
| 模型 | 关键成员 | 说明 |
|---|---|---|
UpdateCheckResult | UpdateFound / CurrentVersion / TargetVersion | 版本校验结果,PackageInfo 为服务端返回的包元数据 |
UpdatePreparationResult | IsReadyToInstall | 一体化流程结果,仅当 Success && State == ReadyToInstall && PackageInfo != null && FilePath != null 时为 true |
InstallationCheckResult | Record / IsInstalled / HasPendingInstallation | 安装结果核对结果 |
InstallationRecord | TargetVersion / RequestedAt / InstalledVersion / ConfirmedAt | 持久化的安装记录,不含下载凭据 |
3.3 实例方法
GeneralUpdateBootstrap(静态工厂):
| 方法名 | 入参明细 | 使用场景 | 注意事项 |
|---|---|---|---|
CreateDefault(options, ...) | options — 更新选项;可选参数:contextProvider, activityProvider, httpClient, versionComparer, eventDispatcher, logger, httpOptions, packageSource, installationStore | 创建默认的 Android 更新引导实例 | 所有可选参数为 null 时使用内置默认实现;DownloadBufferSize <= 0 抛 ArgumentOutOfRangeException |
默认注入链:
| 抽象接口 | 默认实现 |
|---|---|
IAndroidContextProvider | DefaultAndroidContextProvider |
IAndroidActivityProvider | NullAndroidActivityProvider |
IUpdateLogger | NoOpUpdateLogger |
IFileStorage | PhysicalFileStorage |
IUpdateDownloader | HttpResumableApkDownloader |
IHashValidator | Sha256HashValidator |
IApkInstaller | AndroidApkInstaller |
IVersionComparer | SystemVersionComparer |
IUpdatePackageSource | HttpUpdatePackageClient |
IInstallationStore | JsonFileInstallationStore |
IUpdateEventDispatcher | ImmediateEventDispatcher |
IAndroidBootstrap:
| 方法名 | 入参明细 | 返回类型 | 使用场景 | 注意事项 |
|---|---|---|---|---|
PrepareUpdateAsync(currentVersion, ct) | currentVersion — 设备当前版本;ct — 取消令牌 | UpdatePreparationResult | 一体化完成查询、比较、预检查、下载与校验 | 在一个操作锁内完成;Success 为 true 但 IsReadyToInstall 为 false 表示无更新或被跳过;不会打开安装器或权限页面 |
ValidateAsync(currentVersion, ct) | currentVersion — 设备当前版本;ct — 取消令牌 | UpdateCheckResult | 只检查版本,或需要分阶段控制 | 包元数据由 AndroidUpdateOptions.UpdateServer 或注入的包源提供,调用方不再构造 UpdatePackageInfo;返回的 PackageInfo 可直接传给后续方法 |
DownloadAndVerifyAsync(packageInfo, ct) | packageInfo — 更新包信息;ct — 取消令牌 | UpdateOperationResult | 下载 APK 并校验大小与 SHA256 | 成功时 State = ReadyToInstall、FilePath 为 APK 路径 |
LaunchInstallerAsync(packageInfo, apkFilePath, ct) | packageInfo — 更新包信息;apkFilePath — APK 路径;ct — 取消令牌 | InstallResult | 写入安装记录并拉起系统安装器 | 先原子写入安装记录再拉起;写入失败返回 FileIoError 且不拉起安装器;Success = true 只表示安装器已拉起 |
CheckInstallationAsync(currentVersion, ct) | currentVersion — 从 PackageManager 读取的实际版本;ct — 取消令牌 | InstallationCheckResult | 启动时或从安装器返回时核对安装结果 | 不依赖网络;无记录返回 Success && State == None;记录损坏明确报错,不伪装成"无记录" |
ResetInstallationAsync(ct) | ct — 取消令牌 | UpdateOperationResult | 显式清除升级记录(含损坏数据) | 仅清除记录,不修改已安装应用、下载文件或服务端配置 |
AddListenerUpdatePrecheck(func) | func — Func<UpdateInfoEventArgs, bool> | IAndroidBootstrap | 注册更新前回调 | 返回 true 跳过、false 继续;强制更新不调用;可链式注册 |
GetSnapshot() | 无 | UpdateStateSnapshot | 获取当前状态快照 | 返回 (State, FailureReason, Message) |
Dispose() | 无 | void | 释放下载器 / 包源等资源 | 关闭前先取消并等待操作完成;提前 Dispose 会拒绝新调用与排队调用,并延迟到当前操作退出后释放资源 |
3.4 回调事件
| 事件名称 | 回调参数 | 触发时机 | 使用说明 |
|---|---|---|---|
AddListenerValidate | ValidateEventArgs — PackageInfo, CurrentVersion | 版本校验发现可用更新时 | 可用于 UI 展示新版本信息;被 pre-check 跳过时不触发 |
AddListenerDownloadProgressChanged | DownloadProgressChangedEventArgs — ProgressPercentage, DownloadSpeedBytesPerSecond, DownloadedBytes, RemainingBytes, TotalBytes, PackageInfo, StatusDescription | 下载进度更新时 | 可用于进度条展示 |
AddListenerUpdateCompleted | UpdateCompletedEventArgs — Result(UpdateOperationResult) | 下载校验完成(ReadyToInstall)或安装器拉起完成(Installing)时 | 不代表安装成功,仅表示阶段完成 |
AddListenerInstallationConfirmed | InstallationConfirmedEventArgs — Result(InstallationCheckResult) | CheckInstallationAsync 首次持久化确认目标版本已安装时 | 只触发一次,重复核对或进程重启不会重复触发;不是可靠消息队列 |
AddListenerUpdateFailed | UpdateFailedEventArgs — Result(UpdateOperationResult) | 任何阶段失败时 | 包含失败原因和异常信息 |
4. 扩展示例(高阶用法)
4.1 组件可扩展能力总览
所有服务均可通过 CreateDefault 的可选参数替换:
| 接口 | 默认实现 | 说明 |
|---|---|---|
IVersionComparer | SystemVersionComparer | 版本对比策略 |
IUpdatePackageSource | HttpUpdatePackageClient | 更新包元数据来源(协议 / 接口出参) |
IUpdateDownloader | HttpResumableApkDownloader | APK 下载实现 |
IHashValidator | Sha256HashValidator | SHA256 哈希校验 |
IApkInstaller | AndroidApkInstaller | APK 安装触发 |
IFileStorage | PhysicalFileStorage | 文件存储操作 |
IInstallationStore | JsonFileInstallationStore | 安装记录持久化 |
IUpdateEventDispatcher | ImmediateEventDispatcher | 事件调度器(直接调用) |
IUpdateLogger | NoOpUpdateLogger | 日志记录器 |
IAndroidContextProvider | DefaultAndroidContextProvider | Android Context 提供器 |
IAndroidActivityProvider | NullAndroidActivityProvider | Android Activity 提供器 |
ISslValidationPolicy | 通过 HttpDownloadOptions 配置 | SSL 证书验证策略 |
IHttpAuthProvider | 通过 HttpDownloadOptions 或单包配置 | HTTP 认证提供器 |
需要替换全部环节时,可直接使用 AndroidBootstrap 的依赖注入构造函数:
new AndroidBootstrap(
versionComparer, downloader, hashValidator, apkInstaller, fileStorage,
packageSource, installationStore, eventDispatcher?, logger?);
Bootstrap 负责释放实现了 IDisposable 的 downloader 与 package source;其余注入依赖(包括 store)由宿主自行管理,不要跨升级器共享由 Bootstrap 拥有的服务实例。
4.2 分场景示例
场景 1:自定义版本比较策略
【场景说明】应用使用 year.month.day.build 格式版本号,默认的 System.Version 解析会失败,需要自定义版本比较器。
【示例代码】
using GeneralUpdate.Avalonia.Android.Abstractions;
public sealed class CustomDateVersionComparer : IVersionComparer
{
public bool TryCompare(string currentVersion, string targetVersion,
out int compareResult, out string? errorMessage)
{
try
{
var current = ParseVersion(currentVersion);
var target = ParseVersion(targetVersion);
compareResult = target.CompareTo(current);
errorMessage = null;
return true;
}
catch (Exception ex)
{
compareResult = 0;
errorMessage = ex.Message;
return false;
}
}
private static DateTime ParseVersion(string version) =>
DateTime.ParseExact(version, "yyyy.MM.dd.fff", null);
}
// 使用
var bootstrap = GeneralUpdateBootstrap.CreateDefault(
options,
versionComparer: new CustomDateVersionComparer());
【效果&注意事项】
- 返回的
compareResult遵循target.CompareTo(current)语义:正数表示有新版本 - 实现失败时返回
false和错误信息,校验以VersionComparisonFailed失败