实现后台音乐播放并不是给MediaElement设置一个属性就能完成的事情。MAUI的跨平台抽象在音频领域主要覆盖基础播放、进度控制和媒体源管理,但一旦应用进入后台,Android和iOS的系统调度策略会限制普通UI组件的执行,音频往往瞬间中断。要解决这个问题,必须理解两个平台对后台任务的不同约束,并针对性地使用平台原生能力。

理解MAUI后台播放的限制与平台差异
MAUI应用的生命周期在切换后台时并不会完全退出,但系统会降低其运行优先级。Android通常会把应用置于缓存进程,当内存紧张时可能直接杀死进程;iOS则把应用挂起,代码几乎无法继续执行。对于音频播放来说,这种机制意味着普通的页面级播放控件,例如MediaElement,在应用退到后台后很可能被暂停或中断,即便代码里没有显式调用暂停逻辑。
要想让音乐持续播放,核心思路是把音频任务从普通UI组件中剥离出来,交给系统认可的专门通道。Android允许使用前台服务配合媒体会话保持播放,前台服务会显示一条常驻通知,告诉系统这个任务需要在后台继续运行。iOS则通过AVAudioSession的Playback类别和后台音频模式声明,让系统知道这个应用需要继续输出声音。两者的配置方式完全不同,这也是MAUI后台音频无法用一套代码直接覆盖的原因。
基础播放部分可以使用MediaElement快速搭建界面。下面是一个简单的C#创建方式,适合页面内正常播放:
var player = new MediaElement
{
Source = MediaSource.FromUri("https://ipipp.com/sample.mp3"),
ShouldAutoPlay = false
};
player.Play();
这段代码在应用处于前台时没有问题,但当用户点击Home键或切换到其他App后,播放状态就不再可靠。因此对于真正的后台音乐场景,需要继续向平台层深挖。
Android前台服务实现后台持续播放
Android端最稳妥的方案是使用Foreground Service。前台服务与普通后台服务不同,它要求必须关联一个用户可见的通知,系统会因为这个通知而保留应用进程,降低被回收的概率。音频播放服务通常还会接入MediaSession,这样系统媒体面板、耳机线控和锁屏界面都能正确显示播放状态,并提供播放、暂停、下一首等控制能力。
首先要创建一个继承自Service的音频播放服务。在服务中初始化MediaPlayer,创建通知渠道,并调用StartForeground把服务提升为前台状态。以下是一个简化但完整的示例:
[Service(ForegroundServiceType = Android.Content.PM.ForegroundService.TypeMediaPlayback)]
public class AudioService : Service
{
private MediaPlayer _player;
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
CreateNotificationChannel();
var notification = new Notification.Builder(this, "audio_channel")
.SetContentTitle("音乐播放中")
.SetContentText("正在后台播放音乐")
.SetSmallIcon(Resource.Mipmap.appicon)
.SetOngoing(true)
.Build();
StartForeground(1001, notification);
_player = new MediaPlayer();
_player.SetAudioAttributes(new AudioAttributes.Builder()
.SetUsage(AudioUsageKind.Media)
.SetContentType(AudioContentType.Music)
.Build());
_player.SetDataSource("https://ipipp.com/sample.mp3");
_player.Prepare();
_player.Start();
return StartCommandResult.Sticky;
}
private void CreateNotificationChannel()
{
if (OperatingSystem.IsAndroidVersionAtLeast(26))
{
var channel = new NotificationChannel("audio_channel", "音乐播放通知", NotificationImportance.Low);
var manager = (NotificationManager)GetSystemService(NotificationService);
manager.CreateNotificationChannel(channel);
}
}
public override void OnDestroy()
{
_player?.Stop();
_player?.Release();
_player = null;
base.OnDestroy();
}
public override IBinder OnBind(Intent intent) => null;
}
除了编写服务,还需要在AndroidManifest.xml中声明权限和Service节点。Android 13及以上版本需要动态申请POST_NOTIFICATIONS权限,否则前台服务通知可能无法显示,服务也会受到影响。相关清单配置如下:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application>
<service android:name=".AudioService"
android:foregroundServiceType="mediaPlayback"
android:exported="false" />
</application>
启动服务时,应该在用户点击播放按钮的代码中调用StartForegroundService。当用户停止播放或离开应用时,需要调用StopService或让服务内部自行停止。否则服务会一直运行,既消耗电量,也会让通知长期驻留。MediaSession的加入可以进一步优化体验,但它不是后台播放的必需条件,如果只是简单播放音乐,先跑通前台服务已经能够满足主要需求。
iOS端配置后台音频会话
iOS对后台任务管理比Android更加严格,但音频播放属于系统明确支持的少数后台模式之一。只要正确配置AVAudioSession,并声明后台音频能力,即使应用进入后台,AVPlayer也能继续输出声音。这里的关键是将会话类别设置为Playback,而不是默认的SoloAmbient。默认类别在锁屏或切后台时会自动静音。
在AppDelegate的FinishedLaunching方法中完成会话配置:
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
var session = AVAudioSession.SharedInstance();
NSError error;
session.SetCategory(AVAudioSessionCategory.Playback, out error);
session.SetActive(true, out error);
return base.FinishedLaunching(app, options);
}
同时需要在Info.plist中添加UIBackgroundModes键,并声明audio值。这一步会让系统在应用进入后台时继续允许音频播放。配置方式如下:
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
</array>
之后使用AVPlayer播放音频即可。AVPlayer能够感知会话配置,在应用切到后台后保持播放。基本调用如下:
var url = NSUrl.FromString("https://ipipp.com/sample.mp3");
var player = new AVPlayer(url);
player.Play();
需要注意的是,iOS的真机测试必不可少。模拟器对后台音频的模拟并不完全准确,有些配置问题在真机上才会暴露。另外,如果播放远程流媒体地址,还需要确保App Transport Security允许HTTP请求,或者在服务器端启用HTTPS。锁屏控制中心的播放信息则需要实现MPNowPlayingInfoCenter和远程命令处理,这属于进阶优化,基础后台播放暂时可以不加。
跨平台统一封装与调用建议
在MAUI项目中,比较合理的做法是定义一个跨平台接口,把Android和iOS的实现分别放入各自的平台目录。这样做可以让页面代码保持简洁,也便于后续扩展音频控制能力。接口可以很简单,只包含播放、暂停和停止三个基础动作:
public interface IBackgroundAudioService
{
void Play(string url);
void Pause();
void Stop();
}
Android实现类内部启动或停止AudioService,并根据播放状态控制MediaPlayer。iOS实现类则基于AVPlayer完成同样的逻辑。注册依赖时,可以在MauiProgram中使用条件编译选择对应实现:
#if ANDROID
builder.Services.AddSingleton<IBackgroundAudioService, Platforms.Android.AudioServiceWrapper>();
#elif IOS
builder.Services.AddSingleton<IBackgroundAudioService, Platforms.iOS.AudioServiceWrapper>();
#endif
页面通过构造函数注入服务,点击播放按钮时传入远程音频地址。这种方式让业务逻辑不直接依赖平台类型,后续增加播放列表、后台控制或系统媒体集成时也能保持结构清晰。
最后要提醒的是,后台音频必然会带来额外的电量消耗和系统资源占用。开发时应当为用户提供明确的停止播放入口,并且避免在用户没有主动操作的情况下自动启动后台音频。在Android上还要处理好通知权限被拒绝的场景,在iOS上则要避免与其他使用音频会话的功能产生冲突。只有在平台差异被充分理解并配置到位之后,MAUI应用才能像原生音乐播放器一样,在后台稳定地输出声音。