Avalonia使用.NET For Anroid API(以文件下载为例)

EliorFoy Lv2

1. 项目结构

典型Avalonia解决方案结构:

1
2
3
4
5
6
7
8
9
10
MyApp/
├── MyApp/ # 共享项目 (UI和业务逻辑)
│ ├── App.axaml
│ ├── ViewModels/
│ └── Views/
├── MyApp.Android/ # Android平台项目
│ ├── MainActivity.cs
│ ├── AndroidManifest.xml
│ └── Services/ # 这里添加Android特定实现
└── MyApp.Desktop/ # 桌面平台项目

服务注册与调用流程

1
2
3
4
5
6
7
8
9
10
[Android项目]                    [共享项目]
| |
v v
MainActivity.cs -------> App.cs(静态服务容器)
| |
RegisterPlatformServices() GetService<T>()
| |
AndroidFileDownloadService MainViewModel
|
调用下载方法

2. 在共享项目中定义接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// MyApp/Services/IFileDownloadService.cs
using System.Threading.Tasks;

namespace MyApp.Services
{
public interface IFileDownloadService
{
Task<string> DownloadFileAsync(string url, string filename);

// 可选:提供下载进度
Task<string> DownloadFileWithProgressAsync(string url, string filename,
IProgress<double> progress);
}
}

3. 在Android项目中实现接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
// MyApp.Android/Services/AndroidFileDownloadService.cs
using System;
using System.IO;
using System.Threading.Tasks;
using Android.App;
using Android.Content;
using Android.Database;
using Android.OS;
using MyApp.Services;

namespace MyApp.Android.Services
{
public class AndroidFileDownloadService : IFileDownloadService
{
private readonly Context _context;

public AndroidFileDownloadService()
{
_context = Application.Context;
}

public Task<string> DownloadFileAsync(string url, string filename)
{
var tcs = new TaskCompletionSource<string>();

try
{
var downloadManager = DownloadManager.FromContext(_context);
var request = new DownloadManager.Request(Android.Net.Uri.Parse(url));

// 配置下载
request.SetTitle(filename);
request.SetDescription("正在下载文件...");
request.SetNotificationVisibility(DownloadVisibility.VisibleNotifyCompleted);

// 设置下载位置
string destinationPath = Path.Combine(
Environment.GetExternalStoragePublicDirectory(
Environment.DirectoryDownloads).AbsolutePath,
filename);

request.SetDestinationUri(Android.Net.Uri.FromFile(new Java.IO.File(destinationPath)));

// 注册下载完成的广播接收器
var receiver = new DownloadCompletedReceiver(downloadManager, tcs, destinationPath);
_context.RegisterReceiver(
receiver,
new IntentFilter(DownloadManager.ActionDownloadComplete)
);

// 开始下载
long downloadId = downloadManager.Enqueue(request);
receiver.DownloadId = downloadId;
}
catch (Exception ex)
{
tcs.SetException(ex);
}

return tcs.Task;
}

public Task<string> DownloadFileWithProgressAsync(string url, string filename,
IProgress<double> progress)
{
// 实现带进度的下载
// 类似上面的方法,但添加了定期检查进度的逻辑
// ...
throw new NotImplementedException("进度下载尚未实现");
}

// 广播接收器处理下载完成事件
private class DownloadCompletedReceiver : BroadcastReceiver
{
private readonly DownloadManager _downloadManager;
private readonly TaskCompletionSource<string> _tcs;
private readonly string _destinationPath;

public long DownloadId { get; set; }

public DownloadCompletedReceiver(DownloadManager downloadManager,
TaskCompletionSource<string> tcs,
string destinationPath)
{
_downloadManager = downloadManager;
_tcs = tcs;
_destinationPath = destinationPath;
}

public override void OnReceive(Context context, Intent intent)
{
long receivedDownloadId = intent.GetLongExtra(DownloadManager.ExtraDownloadId, -1);

if (receivedDownloadId == DownloadId)
{
// 检查下载状态
DownloadManager.Query query = new DownloadManager.Query();
query.SetFilterById(DownloadId);
ICursor cursor = _downloadManager.InvokeQuery(query);

if (cursor.MoveToFirst())
{
int statusColumnIndex = cursor.GetColumnIndex(DownloadManager.ColumnStatus);
int status = cursor.GetInt(statusColumnIndex);

if (status == (int)DownloadStatus.Successful)
{
_tcs.SetResult(_destinationPath);
}
else
{
_tcs.SetException(new Exception($"下载失败,状态码: {status}"));
}
}

cursor.Close();
context.UnregisterReceiver(this);
}
}
}
}
}

4. 在Android项目中注册服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// MyApp.Android/MainActivity.cs
using Android.App;
using Android.Content.PM;
using Android.OS;
using Avalonia;
using Avalonia.Android;
using MyApp.Services;
using MyApp.Android.Services;

namespace MyApp.Android
{
[Activity(Label = "MyApp",
Theme = "@style/MyTheme.NoActionBar",
Icon = "@drawable/icon",
LaunchMode = LaunchMode.SingleInstance,
ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)]
public class MainActivity : AvaloniaMainActivity<App>
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);

// 注册Android特定服务
App.RegisterPlatformServices(services =>
{
services.AddSingleton<IFileDownloadService, AndroidFileDownloadService>();
});
}
}
}

5. 在共享项目中添加服务注册支持

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// MyApp/App.axaml.cs
using System;
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using Microsoft.Extensions.DependencyInjection;
using MyApp.Services;
using MyApp.ViewModels;
using MyApp.Views;

namespace MyApp
{
public class App : Application
{
private static readonly ServiceCollection _services = new ServiceCollection();
private static IServiceProvider _serviceProvider;

public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}

public override void OnFrameworkInitializationCompleted()
{
// 构建服务提供程序
_serviceProvider = _services.BuildServiceProvider();

// 注册视图模型
_services.AddTransient<MainViewModel>();

// 配置应用程序
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
desktop.MainWindow = new MainWindow
{
DataContext = _serviceProvider.GetRequiredService<MainViewModel>()
};
}

base.OnFrameworkInitializationCompleted();
}

// 用于平台特定服务注册
public static void RegisterPlatformServices(Action<IServiceCollection> configureServices)
{
configureServices(_services);
}

// 获取服务的辅助方法
public static T GetService<T>() where T : class
{
return _serviceProvider?.GetService<T>();
}
}
}

6. 在ViewModel中使用下载服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
// MyApp/ViewModels/MainViewModel.cs
using System;
using System.Threading.Tasks;
using MyApp.Services;
using ReactiveUI;

namespace MyApp.ViewModels
{
public class MainViewModel : ViewModelBase
{
private readonly IFileDownloadService _downloadService;
private string _status;

public string Status
{
get => _status;
set => this.RaiseAndSetIfChanged(ref _status, value);
}

public ReactiveCommand<string, string> DownloadCommand { get; }

public MainViewModel()
{
// 获取平台特定服务
_downloadService = App.GetService<IFileDownloadService>();

DownloadCommand = ReactiveCommand.CreateFromTask<string, string>(DownloadFileAsync);
Status = "准备下载";
}

private async Task<string> DownloadFileAsync(string url)
{
try
{
Status = "开始下载...";

// 如果服务不可用(例如在设计时),则返回错误
if (_downloadService == null)
{
Status = "下载服务不可用";
return null;
}

// 执行下载
string filename = $"download_{DateTime.Now:yyyyMMddHHmmss}.pdf";
string filePath = await _downloadService.DownloadFileAsync(url, filename);

Status = $"下载完成: {filePath}";
return filePath;
}
catch (Exception ex)
{
Status = $"下载失败: {ex.Message}";
return null;
}
}
}
}

7. 在XAML中添加下载按钮

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- MyApp/Views/MainView.axaml -->
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:MyApp.ViewModels"
x:Class="MyApp.Views.MainView">
<Design.DataContext>
<vm:MainViewModel />
</Design.DataContext>

<StackPanel Spacing="20" Margin="20">
<Button Content="下载PDF示例"
Command="{Binding DownloadCommand}"
CommandParameter="https://example.com/sample.pdf" />

<TextBlock Text="{Binding Status}" />
</StackPanel>
</UserControl>

8. 完整流程

  1. 应用启动
  • 首先Android项目的MainActivity初始化

  • 调用App.RegisterPlatformServices()注册Android服务

  1. 服务注册
  • AndroidFileDownloadService注册为IFileDownloadService的实现

  • 这个服务被添加到App类中的静态_services集合

  1. 框架初始化
  • App.OnFrameworkInitializationCompleted()被调用

  • _serviceProvider从_services构建

  • ViewModel被创建和注册

  1. ViewModel获取服务
  • MainViewModel构造函数中调用App.GetService()

  • 返回之前注册的AndroidFileDownloadService实例

  1. 调用下载方法
  • 用户点击UI中的下载按钮

  • ViewModel调用_downloadService.DownloadFileAsync()

  • 实际执行的是AndroidFileDownloadService中的实现

  • 标题: Avalonia使用.NET For Anroid API(以文件下载为例)
  • 作者: EliorFoy
  • 创建于 : 2025-07-10 12:33:37
  • 更新于 : 2025-07-10 12:40:24
  • 链接: https://eliorfoy.github.io/2025/07/10/大三下/Avalonia使用.NET For Anroid API(以文件下载为例)/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论