You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Xamarin.Forms Android本地通知自定义声音及触发问题求助

Xamarin.Forms Android本地通知问题解决方案

我来帮你逐个解决这三个在Xamarin.Forms Android本地通知开发中碰到的棘手问题:


1. 无法正确引用Assets/Resources/raw中的MP3作为自定义通知声音

首先要明确:系统通知服务无法直接访问App的Assets目录,所以Assets里的音频文件没法直接用作通知声音,推荐的做法是把音频放到Resources/raw文件夹(没有的话直接新建),并确保文件的Build Action设置为AndroidResource

正确的Uri获取方式如下:

// 注意:your_sound_file_name不要带.mp3后缀,Resource.Raw会自动生成对应的资源ID
var soundUri = Android.Net.Uri.Parse($"android.resource://{PackageName}/{Resource.Raw.your_sound_file_name}");

如果之前用SetSound时Uri写错,就会导致声音无法加载,用上面的方式就能正确指向raw目录下的音频文件。


2. SetSound废弃后的官方替代方案

从Android 8.0(API 26)开始,所有通知必须归属到一个NotificationChannel,旧的SetSound方法也被官方废弃了,正确的做法是在创建NotificationChannel时配置声音

if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
    const string channelId = "CustomSoundChannel";
    const string channelName = "自定义声音通知频道";
    const string channelDesc = "用于播放自定义通知声音的频道";

    // 创建频道
    var notificationChannel = new NotificationChannel(channelId, channelName, NotificationImportance.Default)
    {
        Description = channelDesc
    };

    // 配置音频属性(指定用途为通知)
    var audioAttributes = new AudioAttributes.Builder()
        .SetContentType(AudioContentType.Sonification)
        .SetUsage(AudioUsageKind.Notification)
        .Build();

    // 设置频道的声音
    var soundUri = Android.Net.Uri.Parse($"android.resource://{PackageName}/{Resource.Raw.your_sound_file_name}");
    notificationChannel.SetSound(soundUri, audioAttributes);

    // 将频道注册到系统通知管理器
    var notificationManager = GetSystemService(NotificationService) as NotificationManager;
    notificationManager?.CreateNotificationChannel(notificationChannel);
}

之后创建通知时,只需要在NotificationCompat.Builder中指定这个channelId,系统就会自动使用频道配置的声音,不需要再调用SetSound方法。


3. 滑掉应用后本地通知无法触发(即使有前台服务)

这个问题主要和Android的后台限制、Doze模式以及厂商定制化的进程查杀机制有关,按以下步骤排查解决:

(1)确保前台服务配置完全正确

  • AndroidManifest.xml中声明必要权限:
    <!-- 前台服务权限 -->
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <!-- Android 13+ 必须添加通知权限 -->
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
    
  • 启动前台服务时,必须显示一个持久化的通知,不能省略这一步,否则系统会判定服务无效并回收。

(2)优化通知触发的调度逻辑

如果用AlarmManager触发通知,在Android 6.0+要使用绕过Doze模式的API:

var alarmManager = (AlarmManager)GetSystemService(AlarmService);
var intent = new Intent(this, typeof(YourNotificationBroadcastReceiver));
// Android 12+ 必须添加Immutable标记
var pendingIntent = PendingIntent.GetBroadcast(this, 0, intent, PendingIntentFlags.UpdateCurrent | PendingIntentFlags.Immutable);

// 根据系统版本选择合适的触发方法
if (Build.VERSION.SdkInt >= BuildVersionCodes.M)
{
    // 精确触发,允许在Doze模式下运行
    alarmManager.SetExactAndAllowWhileIdle(AlarmType.RtcWakeup, triggerTimeInMillis, pendingIntent);
}
else
{
    alarmManager.SetExact(AlarmType.RtcWakeup, triggerTimeInMillis, pendingIntent);
}

如果用WorkManager调度,要关闭不必要的约束:

var workRequest = new OneTimeWorkRequest.Builder(typeof(YourNotificationWorker))
    .SetRequiresBatteryNotLow(false)
    .SetRequiresDeviceIdle(false)
    .Build();
WorkManager.Instance.Enqueue(workRequest);

(3)处理厂商定制化后台限制

国内小米、华为、OPPO等厂商有自己的后台管理机制,滑掉应用后会直接杀死进程,这时候需要引导用户手动将App加入后台白名单/允许自启动,你可以在App内添加一个引导页面,告诉用户具体的操作步骤(不同厂商路径不同)。


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

火山引擎 最新活动