Android 平台 MAUI 应用更新服务

news/2025/9/19 15:38:30/文章来源:https://www.cnblogs.com/dosswy/p/19100966

该代码是面向 Android 平台 的 MAUI(.NET Multi-platform App UI)应用更新服务实现,实现了IUpgradeService接口,核心功能涵盖版本检查、APK 下载与安装,整体设计兼顾异常处理、用户体验与 Android 版本兼容性。

一、添加 IUpgradeService.cs 接口

/// <summary>/// 升级服务接口类/// </summary>public interface IUpgradeService{/// <summary>/// 检查更新/// </summary>/// <param name="latestVersion">最新版本</param>/// <returns></returns>bool CheckUpdatesAsync(string latestVersion);/// <summary>/// 下载安装文件/// </summary>/// <param name="url">下载URL</param>/// <param name="action">进度条处理方法</param>/// <returns></returns>Task DownloadFileAsync(string url, Action<long, long> action, CancellationToken cancellationToken = default);/// <summary>/// 安装APK的方法/// </summary>void InstallNewVersion();}

二、在 Platforms/Android 目录下创建 UpgradeService.cs 文件,实现 IUpgradeService 接口

 public class UpgradeService : IUpgradeService{readonly HttpClient _client;public UpgradeService(){_client = new HttpClient();}/// <summary>/// 检查当前应用版本是否需要更新/// </summary>/// <param name="latestVersion">服务器提供的最新版本号字符串(如 "1.2.3")</param>/// <returns>true表示需要更新,false表示不需要或版本相同</returns>public bool CheckUpdatesAsync(string latestVersion){try{// 获取当前版本(MAUI 6+ 推荐方式)var currentVersionStr = VersionTracking.Default.CurrentVersion;// 使用Version类进行专业比较var current = ParseVersion(currentVersionStr);var latest = ParseVersion(latestVersion);return latest > current;}catch{// 出现任何异常时保守返回true(建议更新)return true;}}/// <summary>/// 安全解析版本字符串(支持不同长度和格式)/// </summary>private Version ParseVersion(string versionStr){// 处理可能的null/emptyif (string.IsNullOrWhiteSpace(versionStr))return new Version(0, 0);// 分割版本号组成部分var parts = versionStr.Split('.').Select(p => int.TryParse(p, out int num) ? num : 0).ToArray();// 根据长度创建Version对象return parts.Length switch{1 => new Version(parts[0], 0),2 => new Version(parts[0], parts[1]),3 => new Version(parts[0], parts[1], parts[2]),4 => new Version(parts[0], parts[1], parts[2], parts[3]),_ => new Version(0, 0)};}/// <summary>/// 下载并安装APK/// </summary>/// <param name="url"></param>/// <param name="progressAction"></param>/// <param name="cancellationToken"></param>/// <returns></returns>/// <exception cref="ArgumentNullException"></exception>public async Task DownloadFileAsync(string url, Action<long, long> progressAction, CancellationToken cancellationToken = default){// 验证输入参数if (string.IsNullOrEmpty(url))throw new ArgumentNullException(nameof(url));if (progressAction == null)throw new ArgumentNullException(nameof(progressAction));// 生成唯一的临时文件名,避免冲突var fileName = "com.jiajing.iotplatform.apk";var tempFilePath = Path.Combine(FileSystem.AppDataDirectory, $"{fileName}.tmp");var finalFilePath = Path.Combine(FileSystem.AppDataDirectory, fileName);try{// 使用using确保HttpClient正确释放(如果不是全局单例的话)using var request = new HttpRequestMessage(HttpMethod.Get, url);// 异步获取响应,支持取消操作using var response = await _client.SendAsync(request,HttpCompletionOption.ResponseHeadersRead,cancellationToken);// 确保请求成功response.EnsureSuccessStatusCode();// 获取文件总大小,处理可能为null的情况var totalLength = response.Content.Headers.ContentLength ?? -1;var downloadedLength = 0L;// 读取响应流并写入文件using var stream = await response.Content.ReadAsStreamAsync(cancellationToken);await using var fileStream = new FileStream(tempFilePath, FileMode.Create, FileAccess.Write, FileShare.None, 8192, true);var buffer = new byte[8192]; // 使用8KB缓冲区(更适合大多数场景)int bytesRead;// 循环读取流,支持取消操作while ((bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) > 0){// 写入文件await fileStream.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false);// 更新进度downloadedLength += bytesRead;progressAction(downloadedLength, totalLength);}// 确保所有数据都写入磁盘await fileStream.FlushAsync(cancellationToken).ConfigureAwait(false);// 下载完成后重命名临时文件(避免部分下载的文件被误认)if (File.Exists(finalFilePath))File.Delete(finalFilePath);File.Move(tempFilePath, finalFilePath);}catch (Exception){// 处理其他异常if (File.Exists(tempFilePath))File.Delete(tempFilePath);throw;}}/// <summary>/// 安装新版本/// </summary>public void InstallNewVersion(){var file = $"{FileSystem.AppDataDirectory}/{"com.jiajing.iotplatform.apk"}";var apkFile = new Java.IO.File(file);var intent = new Intent(Intent.ActionView);// 判断Android版本if (Build.VERSION.SdkInt >= BuildVersionCodes.N){//给临时读取权限intent.SetFlags(ActivityFlags.GrantReadUriPermission);var uri = FileProvider.GetUriForFile(AndroidApp.Application.Context, "com.jiajing.iotplatform.fileprovider", apkFile);// 设置显式 MIME 数据类型intent.SetDataAndType(uri, "application/vnd.android.package-archive");}else{intent.SetDataAndType(AndroidNet.Uri.FromFile(new Java.IO.File(file)), "application/vnd.android.package-archive");}//指定以新任务的方式启动Activityintent.AddFlags(ActivityFlags.NewTask);//激活一个新的ActivityAndroidApp.Application.Context.StartActivity(intent);}}

三、修改 MauiProgram.cs 文件,注册升级服务

#if ANDROID 是必不可少的,该条件编译指令能确保相关逻辑只在 Android 环境中执行.

    
public static class MauiProgram
{public static MauiApp CreateMauiApp(){ #if ANDROID    // Android 添加自定义升级服务builder.Services.AddSingleton<IUpgradeService, Platforms.Android.UpgradeService>();#endif } 
}

四、添加检查更新方法

Razor页面
    <!--版本更新弹框--><Modal Title="检测到新版本,是否更新?"@bind-Visible="@UpdateConfirm"Closable="false"Centered="true"OnOk="@OnConfirmUpdateAsync"><Space>【版本】: V @AppVersion.Version</Space><Space>【内容】:@AppVersion.ReleaseNotes</Space></Modal><!--下载进度弹框--><Modal Title="正在更新,请稍后..."@bind-Visible="@UpdateDialog"Closable="false" MaskClosable="false"Centered="true"Footer=null><Space>下载进度: @BytesReceived KB / @TotalBytesToReceive KB </Space><AntDesign.Progress StrokeWidth="20" Percent="@Percent" /></Modal>
Razor页面
/// <summary>
/// 最新版本信息
/// </summary>
public AppVersionInfo AppVersion = new();/// <summary>
/// 进度百分比
/// </summary>
public int Percent { get; set; }/// <summary>
/// 总字节数
/// </summary>
public long TotalBytesToReceive { get; set; }/// <summary>
///  已下载字节数
/// </summary>
public long BytesReceived { get; set; }/// <summary>
/// 是否显示进度框
/// </summary>
public bool UpdateDialog;/// <summary>
/// 升级提示并确认框
/// </summary>
public bool UpdateConfirm;/// <summary>
/// 检查是否软件是否为最新版本
/// </summary>
/// <returns></returns>
public async Task GetVersionNew()
{try{// 数据服务:调用编辑服务var response = await VersionService.GetVersionAsync();if (response != null && response.Code == ApiCode.Success){AppVersion = response.Data;// 检查是否为最新版本,非最新版本则提示更新if (UpgradeService.CheckUpdatesAsync(AppVersion.Version)){UpdateConfirm = true;}}}catch (Exception e){await dialogService.ErrorSnackbarAsync($"读取最新版本报错:{e.Message}");}
}/// <summary>
/// 下载新版本并安装
/// </summary>
/// <returns></returns>
public async Task OnConfirmUpdateAsync()
{try{// 启动版本升级 UpdateDialog = true;// 下载文件await UpgradeService.DownloadFileAsync(AppVersion.DownloadUrl, DownloadProgressChanged);// 安装新版本UpgradeService.InstallNewVersion();}catch (Exception e){await dialogService.ErrorSnackbarAsync($"下载新版本及安装时报错:{e.Message}");}finally{UpdateDialog = false;}
}/// <summary>
/// 下载进度
/// </summary>
/// <param name="readLength"></param>
/// <param name="allLength"></param>
private void DownloadProgressChanged(long readLength, long allLength)
{InvokeAsync(() =>{var c = (int)(readLength * 100 / allLength);// 刷新进度为每5%更新一次if (c > 0 && c % 5 == 0){Percent = c; //下载完成百分比BytesReceived = readLength / 1024; //当前已经下载的KbTotalBytesToReceive = allLength / 1024; //文件总大小KbStateHasChanged();}});
}#endregion

五、效果图

image image

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/907864.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SQLSERVER数据备份 - 实践

SQLSERVER数据备份 - 实践2025-09-19 15:37 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; f…

SQL脚本:查询指定SQL的统计信息(cursor,awr)

我们的文章会在微信公众号IT民工的龙马人生和博客网站( www.htz.pw )同步更新 ,欢迎关注收藏,也欢迎大家转载,但是请在文章开始地方标注文章出处,谢谢! 由于博客中有大量代码,通过页面浏览效果更佳。SQL脚本:查询…

k8s学习笔记8——Service

k8s学习笔记8——Service容器带来的问题自动调度: 在Pod创建之前,用户无法预知Pod所在节点以及Pod的IP地址 一个已经存在的Pod在运行过程中,出现故障,Pod也会在新的节点使用新的IP进行部署 应用程序访问服务时,地址是不…

逆向分析之if语句与循环语句的分析

前言 本次我们要介绍if语句,for循环编译后的反汇编内容,以C/C++编写的可执行程序为例进行分析 一只Demo 首先是一只Demo,是我们本次分析对象的源码 #include <stdio.h>void if_demo(int v) {if (v > 5)pri…

读书笔记:索引组织表(IOT):让数据库查询飞起来的黑科技

我们的文章会在微信公众号IT民工的龙马人生和博客网站( www.htz.pw )同步更新 ,欢迎关注收藏,也欢迎大家转载,但是请在文章开始地方标注文章出处,谢谢! 由于博客中有大量代码,通过页面浏览效果更佳。本文为个人学…

AI 自动化智能体训练营

课程背景与解决的问题 你是否也有这样的困扰? 每天被大量重复劳动占据时间? 报表、PPT、文案写得慢,效率低? 想用 AI 提高效率,却不知道从哪入手? 想做副业/创业,但缺乏技术与工具? 👉 这门训练营,将带你从…

ROMA-iOS适配深色模式总结

一、背景深色模式在低光环境下(如夜间使用)可以显著减少屏幕发出的蓝光,降低眼睛疲劳,减轻视觉压力。深色背景配合浅色文字能提供更好的对比度和可读性,减少眩光,让内容更易于阅读。深色模式还可以显著节省电量,…

本地(或自下载)浏览器插件 安装指南

1 首先准备好安装包 通过不用渠道下载所要安装插件的压缩包 2 安装流程 第一步 解压 解压压缩包至文件夹中,请确保您的电脑上已安装解压工具,否则会解压失败 1)mac系统中,双击压缩包即可完成解压。 2)win系统解压…

Docker是什么?最全Docker使用教程(小白到高手) - 实践

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

408学习之c语言(结构体) - 教程

408学习之c语言(结构体) - 教程pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco&q…

路由查看命令

路由查看命令多网卡,软路由设置route print //查看当前路由表route print -4 //查看当前IPv4路由表网络目标,网络掩码,网关,跃点数route print //查看当前路由表route print -4 //查看当前IPv4路由表route delete …

Linux 基础命令01

一、线上查询及帮助命令 1.1 man root@ubuntu2204:~/test 14:28:16 # man ls1.2 help root@ubuntu2204:~/test 14:30:00 # ls --help1.3 info root@ubuntu2204:~/test 14:30:47 # info ls二、文件和目录操作命令 2.1 l…

Git 指令笔记 - 详解

Git 指令笔记 - 详解pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "…

Syntax Error: TypeError: eslint.CLIEngine is not a constructor 解决方案

Syntax Error: TypeError: eslint.CLIEngine is not a constructor 解决方案 删除 package-lock.json 然后 删除 node_modules 从新 npm install 然后再revert package-lock.json (别动项目文件)(如果按新包了,再区…

TDMQ CKafka 版客户端实战指南系列之一:生产最佳实践

TDMQ CKafka 版客户端实战指南系列之一:生产最佳实践导语 在当今数字化时代,数据的产生和流动呈爆发式增长,消息队列作为一种高效的数据传输和处理工具,在各种应用场景中发挥着关键作用。TDMQ CKafka 版作为一款分…

【ACM出版】2025年人工智能、虚拟现实与交互设计国际学术会议(AIVRID 2025)

2025年人工智能、虚拟现实与交互设计国际学术会议(AIVRID 2025)将于2025年10月17-19日在中国广东省东莞市召开。【ACM出版社出版-高录用,快检索-最快见刊后1个月EI & Scopus检索】 【海内外高校、IEEE Fellow等…

《sklearn机器学习——特征提取》 - 指南

《sklearn机器学习——特征提取》 - 指南pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "M…

Windows 10 11 Boot Fix

修復啟動程式碼Bootrec.exe 例如主開機記錄、開機設定資料儲存 (BCD) 和開機磁區。 更新主開機記錄和開機磁區程式碼來修復問題。修復引導程式碼 步驟1.按「Windows + S」開啟搜尋框。然後,在其中鍵入cmd 或命令提示符…

ubuntu 安装 milvus

docker 是 引擎,是底层的基础工具。它用于管理单个容器(Container)的生命周期(构建、运行、停止、删除)。 docker-compose 是 编排工具,是上层的操作界面。它用于定义和运行由多个容器组成的、相互关联的整套应用…

完整教程:MySQL并发问题解析

完整教程:MySQL并发问题解析2025-09-19 15:02 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important…