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

Xamarin.Forms推送通知条件处理:如何实现仅首位接收者可操作通知

可行实现方案

针对你当前的Xamarin.Forms跨端应用推送仅首用户可处理的需求,以下是3种可落地的实现方案:


方案1:服务端全局抢占校验(推荐,准确性最高)

实现逻辑

  • 推送时给每一条通知生成全局唯一的通知ID,写入Firebase/Azure推送的payload中
  • 服务端使用Redis、数据库乐观锁等原子能力存储每条通知的处理状态(未处理/已被用户抢占)
  • 各端收到通知后第一时间请求服务端抢占接口,仅抢占成功的用户可弹出通知、走后续处理逻辑,抢占失败直接丢弃通知

代码修改示例

Android端调整

public override async void OnMessageReceived(RemoteMessage message)
{
    try
    {
        base.OnMessageReceived(message);
        string messageBody = message.GetNotification()?.Body;
        VideoCallMessage videoCallMessage = JsonConvert.DeserializeObject<VideoCallMessage>(messageBody);
        // 新增抢占逻辑
        var canProcess = await YourBackendApi.PreemptNotificationProcess(videoCallMessage.NotifyUniqueId, App.UserContext.UserId);
        if (App.UserContext.IsEmployee && canProcess)
        {
            SendLocalNotification(messageBody);
        }
    }
    catch (Exception ex)
    {
        Log.Debug("FirebaseService.OnMessageReceived()", $"Exception in OnMessageReceived(). ErrorMessage: {ex.Message}, Stack Trace: {ex.StackTrace}");
        Microsoft.AppCenter.Crashes.Crashes.TrackError(ex);
    }
}

iOS端调整

[Export("userNotificationCenter:willPresentNotification:withCompletionHandler:")]
public async void WillPresentNotification(UNUserNotificationCenter center, UNNotification notification, Action<UNNotificationPresentationOptions> completionHandler)
{
    NSDictionary userInfo = notification.Request.Content.UserInfo;
    // 解析通知唯一ID
    var notifyId = userInfo.ObjectForKey(new NSString("NotifyUniqueId")).ToString();
    var canProcess = await YourBackendApi.PreemptNotificationProcess(notifyId, App.UserContext.UserId);
    if (canProcess)
    {
        ProcessNotification(userInfo);
        completionHandler(UNNotificationPresentationOptions.Sound | UNNotificationPresentationOptions.Alert);
    }
    else
    {
        // 抢占失败不弹出通知
        completionHandler(UNNotificationPresentationOptions.None);
    }
}

[Export("userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:")]
public async void DidReceiveNotificationResponse(UNUserNotificationCenter center, UNNotificationResponse response, Action completionHandler)
{
    NSDictionary userInfo = response.Notification.Request.Content.UserInfo;
    var notifyId = userInfo.ObjectForKey(new NSString("NotifyUniqueId")).ToString();
    var canProcess = await YourBackendApi.PreemptNotificationProcess(notifyId, App.UserContext.UserId);
    if (canProcess)
    {
        ProcessNotification(userInfo);
    }
    completionHandler();
}

注意

服务端抢占接口必须实现原子操作,比如用Redis的SETNX命令,确保同一时间只有一个请求能返回抢占成功。


方案2:客户端本地幂等校验(改造成本最低)

实现逻辑

  • 推送payload新增全局唯一通知ID和处理有效期字段
  • 客户端本地缓存所有已处理/已过期的通知ID,收到通知后先查本地缓存,存在则直接忽略,不存在则写入缓存后走处理逻辑

优缺点

  • 优点:无需改造服务端接口,仅修改端侧代码即可落地
  • 缺点:极端并发场景下可能出现多个用户同时抢到处理权的情况,适合对准确性要求不是100%的业务场景

代码修改示例

借助Xamarin.Essentials的Preferences实现本地缓存:

// 安卓/iOS端收到通知后统一新增逻辑
var notifyId = videoCallMessage.NotifyUniqueId;
var cacheKey = $"processed_notify_{notifyId}";
if (Preferences.ContainsKey(cacheKey))
{
    return;
}
// 写入缓存标记已处理
Preferences.Set(cacheKey, true);
// 后续走原有处理逻辑

可新增定时任务清理超过有效期的缓存,避免本地存储冗余。


方案3:推送层定向顺序分发(推送资源消耗最低)

实现逻辑

  • 服务端不直接群发通知,先拉取群组内用户的在线状态,按优先级顺序逐个发送推送
  • 每发送一个推送后等待1-2秒回调,若收到用户的处理成功回调则停止后续推送,未收到则继续发送给下一个用户

优缺点

  • 优点:不会给不需要处理的用户发推送,节省推送资源,无无效通知打扰
  • 缺点:需要维护用户在线状态、推送回调逻辑,整体改造成本最高,通知触达延迟相对更高

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 12:27:03