You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET Maui/Xamarin商品售卖APP后台获报价无需刷新方案咨询

.NET Maui/Xamarin 实时报价推送最优实现方案

一、核心思路

要实现卖家端实时接收并展示客户报价,核心是建立客户端与服务器的双向实时通信通道,结合MVVM的Observable机制自动刷新UI。避免轮询的低效,优先选择长连接类方案,同时处理移动端后台保活的兼容性问题。

二、实时通信选型

根据场景推荐两种主流方案:

  • SignalR:适合.NET技术栈的服务器(ASP.NET Core),封装了WebSocket、Server-Sent Events等底层协议,自动处理重连、心跳,开发成本低。
  • MQTT:轻量级消息协议,适合低带宽、设备资源有限的场景,支持离线消息缓存,跨平台兼容性强。

以下以SignalR为例展开实现,MQTT的核心逻辑类似(替换为MQTT客户端监听消息)。

三、MVVM结合实时通信的实现步骤

1. 构建ViewModel与Observable集合

ViewModel继承ObservableObject(CommunityToolkit.Mvvm包提供,简化INotifyPropertyChanged实现),维护ObservableCollection<Quote>用于绑定UI:

using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.AspNetCore.SignalR.Client;
using System.Collections.ObjectModel;
using System.Threading.Tasks;

namespace YourApp.ViewModels
{
    public partial class SellerQuotesViewModel : ObservableObject
    {
        private readonly HubConnection _hubConnection;
        
        [ObservableProperty]
        private ObservableCollection<Quote> _quotes = new();

        public SellerQuotesViewModel()
        {
            // 初始化SignalR连接
            _hubConnection = new HubConnectionBuilder()
                .WithUrl("https://your-api-domain/quoteHub") // 替换为你的SignalR Hub地址
                .Build();

            // 监听服务器推送的新报价
            _hubConnection.On<Quote>("ReceiveNewQuote", async (newQuote) =>
            {
                // 必须在主线程更新UI集合
                await MainThread.BeginInvokeOnMainThread(() =>
                {
                    // 将新报价插入到列表顶部,优先展示
                    Quotes.Insert(0, newQuote);
                });
            });

            // 启动连接并处理重连
            _ = StartConnectionAsync();
        }

        private async Task StartConnectionAsync()
        {
            try
            {
                await _hubConnection.StartAsync();
            }
            catch
            {
                // 连接失败后延迟5秒重试
                await Task.Delay(5000);
                await StartConnectionAsync();
            }
        }

        // 页面销毁时关闭连接,释放资源
        public async Task Cleanup()
        {
            if (_hubConnection.State == HubConnectionState.Connected)
            {
                await _hubConnection.StopAsync();
            }
            _hubConnection.Dispose();
        }
    }

    // 报价实体类
    public class Quote
    {
        public string Id { get; set; }
        public string ProductId { get; set; }
        public decimal OfferPrice { get; set; }
        public string CustomerName { get; set; }
        public DateTime CreatedAt { get; set; }
    }
}

2. UI绑定与页面生命周期处理

在XAML页面中绑定ViewModel的Quotes集合,并在页面销毁时调用Cleanup释放连接:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:vm="clr-namespace:YourApp.ViewModels"
             x:Class="YourApp.Views.SellerQuotesPage">
    <ContentPage.BindingContext>
        <vm:SellerQuotesViewModel/>
    </ContentPage.BindingContext>

    <ListView ItemsSource="{Binding Quotes}"
              HasUnevenRows="True">
        <ListView.ItemTemplate>
            <DataTemplate>
                <ViewCell>
                    <StackLayout Padding="12" BackgroundColor="White">
                        <Label Text="{Binding CustomerName}" FontSize="16" FontAttributes="Bold"/>
                        <Label Text="{Binding OfferPrice, StringFormat='报价: {0:C}'}" FontSize="14" Margin="0,4,0,0"/>
                        <Label Text="{Binding CreatedAt, StringFormat='提交时间: {0:yyyy-MM-dd HH:mm}'}" FontSize="12" TextColor="Gray" Margin="0,4,0,0"/>
                    </StackLayout>
                </ViewCell>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</ContentPage>

页面后台代码处理生命周期:

protected override async void OnDisappearing()
{
    base.OnDisappearing();
    if (BindingContext is SellerQuotesViewModel vm)
    {
        await vm.Cleanup();
    }
}

3. 服务器端SignalR Hub实现(ASP.NET Core)

服务器端需要创建Hub来处理报价推送:

using Microsoft.AspNetCore.SignalR;
using System.Threading.Tasks;

namespace YourApi.Hubs
{
    public class QuoteHub : Hub
    {
        // 客户端调用此方法提交报价,服务器推送给对应卖家
        public async Task SendQuoteToSeller(Quote quote, string sellerId)
        {
            // 根据卖家Id推送,可结合用户身份验证获取当前卖家Id
            await Clients.User(sellerId).SendAsync("ReceiveNewQuote", quote);
        }
    }
}

在Program.cs注册Hub:

builder.Services.AddSignalR();
app.MapHub<QuoteHub>("/quoteHub");

四、移动端后台保活处理

移动端系统会限制后台进程,需要针对性处理:

  • Android:注册Foreground Service,将SignalR连接放在Service中运行,避免被系统回收。Service需要显示通知(Android 8.0+要求)。
  • iOS:由于后台限制严格,长连接难以持续。建议结合APNs推送:当有新报价时,服务器发送推送通知,App收到通知后触发API拉取最新报价并更新Observable集合;iOS 13+可使用Background Tasks框架在后台短暂执行拉取操作。

五、替代方案:定时轮询(适合简单场景)

如果实时通信成本较高,可采用定时轮询方案(缺点是延迟高、耗电):

using System.Timers;

public partial class SellerQuotesViewModel : ObservableObject
{
    private readonly IQuoteService _quoteService;
    private Timer _pollTimer;

    public SellerQuotesViewModel(IQuoteService quoteService)
    {
        _quoteService = quoteService;
        Quotes = new ObservableCollection<Quote>();
        // 每10秒轮询一次
        _pollTimer = new Timer(10000);
        _pollTimer.Elapsed += async (s, e) => await FetchNewQuotes();
        _pollTimer.Start();
    }

    private async Task FetchNewQuotes()
    {
        // 调用API获取未查看的新报价
        var newQuotes = await _quoteService.GetUnseenQuotesAsync();
        if (newQuotes.Any())
        {
            await MainThread.BeginInvokeOnMainThread(() =>
            {
                foreach (var quote in newQuotes)
                {
                    Quotes.Insert(0, quote);
                }
            });
        }
    }
}

内容的提问来源于stack exchange,提问作者MHamitay

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 20:00:58