惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
S
Secure Thoughts
C
Cyber Attacks, Cyber Crime and Cyber Security
SecWiki News
SecWiki News
AWS News Blog
AWS News Blog
P
Proofpoint News Feed
H
Help Net Security
I
InfoQ
T
Tor Project blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Schneier on Security
Schneier on Security
The GitHub Blog
The GitHub Blog
S
Schneier on Security
The Hacker News
The Hacker News
博客园 - Franky
雷峰网
雷峰网
S
Security @ Cisco Blogs
Google Online Security Blog
Google Online Security Blog
Engineering at Meta
Engineering at Meta
G
GRAHAM CLULEY
Security Latest
Security Latest
The Cloudflare Blog
M
MIT News - Artificial intelligence
云风的 BLOG
云风的 BLOG
K
Kaspersky official blog
C
Cisco Blogs
Microsoft Security Blog
Microsoft Security Blog
宝玉的分享
宝玉的分享
Scott Helme
Scott Helme
腾讯CDC
阮一峰的网络日志
阮一峰的网络日志
A
Arctic Wolf
Latest news
Latest news
Last Week in AI
Last Week in AI
The Register - Security
The Register - Security
Google DeepMind News
Google DeepMind News
博客园 - 聂微东
T
Threatpost
G
Google Developers Blog
有赞技术团队
有赞技术团队
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
L
LINUX DO - 热门话题
aimingoo的专栏
aimingoo的专栏
S
Securelist
WordPress大学
WordPress大学
N
Netflix TechBlog - Medium
Y
Y Combinator Blog
T
The Exploit Database - CXSecurity.com
月光博客
月光博客
Recent Announcements
Recent Announcements

博客园 - China Soft

.NET 程序保护实战系列01-流水线架构与保护引擎总览 试官问:你用 AI 编程半年了,那怎么保证 Claude Code 写出来的代码是对的? C#实现控制台多区域输出 园友特惠| 1Panel 企业版 & AI 一体机限量放售,最高直降 5100! 上位机程序发布打包成安装包---Inno Setup 这是一篇学习笔记,分享给大家,如果大家喜欢,也会开启USB的专辑 我问了 AI 一个问题:编码能力贬值后,什么能力值钱? Visual Studio 2026(VS2026) 密钥/激活码 Dddify:给 ASP.NET Core 项目一套轻量、清晰、可落地的 DDD 基础设施 C# ESP32/STM32 轻量 Web 能力库:PicoServer.Nano 高性能百度OCR ONNX Runtime C#实现 别再说 AI 开发就是调接口了!5 种主流模式一次讲清 使用Cursor实现管理系统登录界面的快速开发 在 Avalonia 中编写高性能动画 傻子可懂的 Harness Engineering 入门教程 + 项目实战,一次搞懂 AI 编程工程化! 提升 Text2SQL 准确率 AI/Vibe Coding,本质是软件人工时代向软件工业时代发展 体验优先:十分钟使用 Python+LangChain 玩转阿里通义千问 大模型基础(二):必懂5大基础概念《Token、上下文窗口、Embedding、预训练、微调》 大模型基础(一):什么是LLM? Prompt、Agent、Function Call、Skill、MCP,傻傻分不清楚? 那些喊着AI 要淘汰你的人,正在靠你的焦虑赚大钱! 作为一线研发,我快被全网的 AI 焦虑逼疯了。 Qt QtWebEngine 白屏的解决方案 都是微软亲儿子,WPF凭啥干不掉WinForm?这3个场景说明白了 .NET 高级开发 | C# 中的动态代码:反射、EMIT、表达式树、Roslyn、Source Generators 大模型看大模型:推理Token的能耗用电量比对 WorkBuddy:从“我是谁”到“帮我干活” .NET 高级开发 | 开发 .NET 诊断工具、链路追踪原理 Prompt 焚诀——一个模板,终结你和 AI 的所有沟通问题 [项目]涉案资产信息化管理平台(山东某执法单位定制开发) 大模型RAG实战,从被骂不靠谱到成为部门MVP,这是我的踩坑全记录 白帽子为什么几乎都绕不开 httpx:一款 HTTP 资产探测工具的技术价值 从Prompt工程到Skill工程:Agent Skills开放标准彻底改变了AI协作方式 微软竟然出了免费的 AI 应用开发课?!我已经学上了 一文彻底搞懂 OpenClaw 的架构设计与运行原理(万字图文) AI到底聪明在哪——从手机人脸识别说起 硬核科普:为何小米任天堂封杀 Root?揭秘操作系统从启动到“夺权”的底层游戏
MAUI项目在Android平台通过U盘实现软件更新
China Soft · 2026-03-05 · via 博客园 - China Soft

需求

项目使用MAUI开发的用Android工控机进行相关功能的实现。作为操作设备的屏幕嵌入到仪器中,要使用串口操作实现对仪器的控制。想要实现在有软件更新时,可以通过U盘实现对软件的升级。

功能实现

这里需要使用到 FileProvider,在Android 7之后出于安全考虑不再支持content://URL 或file:///URL这种文件访问方式。在Platforms/Android中主要添加/修改下面两个文件:

  • file_paths.xml
  • AndroidMainfest.xml

image

Platforms/Android/Resources下面新建xml文件夹,并添加 provider_paths.xml文件。

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<root-path name="root" path="" />
<files-path name="files" path="" />
<cache-path name="cache" path="" />
<external-path name="camera_photos" path="" />
<external-files-path name="external_file_path" path="" />
<external-cache-path name="external_cache_path" path="" />
</paths>

修改Platforms/Android下面的AndroidManifest.xml文件,在application下添加provider,再添加一个安卓安装的权限

  • REQUEST_INSTALL_PACKAGES(安装应用)
  • WRITE_EXTERNAL_STORAGE(写入外部存储中的文件)
  • READ_EXTERNAL_STORAGE(读取外部存储中的文件)

AndroidManifest.xml文件内容如下:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application android:allowBackup="true" android:icon="@mipmap/appicon" android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true">
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.companyname.mauiupdateapp.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
</manifest>

在MainPage页面添加一个按钮用于实现软件安装功能,简化了项目实现,没有采用MVVM模式,直接通过Clicked事件实现软件安装。

<Button
x:Name="UpdateBtn"
Clicked="OnCheckUpdateClicked"
HorizontalOptions="Fill"
Text="安装Apk" />

按钮事件实现:

private async void OnCheckUpdateClicked(object? sender, EventArgs e)
{
await CheckForUpdates();
}
 
private async Task CheckForUpdates()
{
try
{
UpdateBtn.IsEnabled = false;
await _updateService.InstallUpdateAsync();
}
catch (Exception ex)
{
await DisplayAlertAsync("错误", $"异常: {ex.Message}", "确定");
}
finally
{
UpdateBtn.IsEnabled = true;
}
}

更新服务实现:

public class UpdateService
{
public async Task InstallUpdateAsync()
{
// 调用平台特定的更新逻辑
await UpdateHandlerFactory.Create().InstallUpdateAsync();
}
}
 
 
public interface IUpdateHandler
{
Task InstallUpdateAsync();
}
 
public static class UpdateHandlerFactory
{
public static IUpdateHandler Create()
{
#if ANDROID
return new MauiUpdateApp.Platforms.Android.UpdateHandler();
#else
return new DefaultUpdateHandler();
#endif
}
}
 
public class DefaultUpdateHandler : IUpdateHandler
{
public async Task InstallUpdateAsync()
{
// 默认实现,非 Android 平台使用
if (App.Current?.MainPage != null)
{
await App.Current.MainPage.DisplayAlertAsync("更新", "此平台不支持自动更新", "确定");
}
}
}

实现Android平台的安装Apk功能:

using Android.Content;
using MauiUpdateApp.Services;
using Android.App;
namespace MauiUpdateApp.Platforms.Android;
public class UpdateHandler : IUpdateHandler
{
private static readonly HttpClient client = new HttpClient();
 
public async Task InstallUpdateAsync()
{
try
{
var activity = Platform.CurrentActivity;
if (activity == null)
{
if (App.Current?.MainPage != null)
{
await App.Current.MainPage.DisplayAlertAsync("错误", "无法获取当前活动", "确定");
}
return;
}
 
PickOptions options = new() { PickerTitle = "Please select a comic file", };
var results = await FilePicker.Default.PickAsync(options);
 
if (results is null)
{
return;
}
 
// 安装 APK
InstallApk(results.FullPath, activity);
}
catch (Exception ex)
{
// 处理错误
Console.WriteLine($"更新失败: {ex.Message}");
if (App.Current?.MainPage != null)
{
await App.Current.MainPage.DisplayAlertAsync("错误", $"更新失败: {ex.Message}", "确定");
}
}
}
 
private static void InstallApk(string apkPath, Activity? activity)
{
var file = new Java.IO.File(apkPath);
var uri = AndroidX.Core.Content.FileProvider.GetUriForFile(activity, $"{activity.PackageName}.fileprovider", file);
 
var intent = new Intent(Intent.ActionView);
intent.SetDataAndType(uri, "application/vnd.android.package-archive");
intent.AddFlags(ActivityFlags.GrantReadUriPermission);
intent.AddFlags(ActivityFlags.NewTask);
 
activity.StartActivity(intent);
}
}

以上为整个项目实现安装Apk的代码,想要获取源码的话,可以从
https://github.com/mzy666888/MauiUpdateApp中获取,欢迎给个Star

参考:https://www.cnblogs.com/MASA/p/16612541.html