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

MAUI中WebView音频跳转/后台播放中断问题(Android)

MAUI Android WebView音频后台播放问题解决

问题描述

在MAUI项目中使用WebView播放音频,iOS平台能正常在跳转页面、切换应用或锁屏时持续播放,但Android端会出现音频暂停的情况。补充说明:音频在弹窗窗口中播放,关闭弹窗后音频可正常播放,但跳转至其他ContentPage时音频停止。

现有代码

XAML布局

<WebView
    x:Name="audio_webview" 
    BackgroundColor="Transparent" 
    Navigated="OnWebViewNavigated"
    HorizontalOptions="FillAndExpand"
    VerticalOptions="CenterAndExpand" >
    <WebView.HeightRequest>
        <OnIdiom x:TypeArguments="x:Double">
            <OnIdiom.Phone>80</OnIdiom.Phone>
            <OnIdiom.Tablet>120</OnIdiom.Tablet>
            <OnIdiom.Desktop>80</OnIdiom.Desktop>
        </OnIdiom>
    </WebView.HeightRequest>
    <WebView.WidthRequest>
        <OnIdiom x:TypeArguments="x:Double">
            <OnIdiom.Phone>420</OnIdiom.Phone>
            <OnIdiom.Tablet>630</OnIdiom.Tablet>
            <OnIdiom.Desktop>420</OnIdiom.Desktop>
        </OnIdiom>
    </WebView.WidthRequest>
</WebView>

C#逻辑

public DailyAudioPopUpPage(string audioUrl)
{
    InitializeComponent();
    LoadAudio(audioUrl);
}

private void LoadAudio(string audioUrl)
{
    var html = $@"
    <html>
    <body style='margin:0; background-color: transparent; display:flex; justify-content:center; align-items:center; height:100%;'>
        <audio controls autoplay style='width:90%;'>
            <source src='{audioUrl}' type='audio/mpeg'>
            Your browser does not support the audio element.
        </audio>
    </body>
    </html>";

    audio_webview.Source = new HtmlWebViewSource { Html = html };
}

跳转页面时的日志输出

11:04:37:012    [AAudio] AAudioStream_requestStop(s#1) called
11:04:37:012    [AAudio] AAudioStream_close(s#1) called ---------------
11:04:37:012    [AAudioStream] setState(s#1) from 10 to 11
11:04:37:012    [AudioTrack] stop(portId:2774, sessionId:48889): 0xb400006eb01c3f40, prior state:STATE_STOPPED
11:04:37:012    [AudioTrackShared] this(0xb400006e500b8550), mCblk(0x6d7fcdc000), front(311736), mIsOut 1, interrupt() FUTEX_WAKE
11:04:37:012    [AudioTrack] requestExitAndWait start
11:04:37:012    [AudioTrack] requestExitAndWait end
11:04:37:012    [AudioTrack] stopAndJoinCallbacks(2774) done
11:04:37:012    [AudioTrack] ~AudioTrack(2774): 0xb400006eb01c3f40
11:04:37:072    [AudioTrack] getpackName client name com.companyname.appname(24146)
11:04:37:072    [AudioTrack] stop(portId:2774, sessionId:48889): 0xb400006eb01c3f40, prior state:STATE_STOPPED
11:04:37:072    [AudioTrack] stopAndJoinCallbacks(2774) done
11:04:37:072    [AAudioStream] setState(s#1) from 11 to 12
11:04:37:072    [AAudioStream] ~AudioStream(s#1) mPlayerBase strongCount = 2
11:04:37:072    [AAudio] AAudioStream_close(s#1) returned 0 ---------

日志说明:这段日志显示Android系统主动停止并关闭了AAudio音频流,原因是原页面跳转后WebView被系统回收,关联的音频资源也被释放。


解决方案

1. 配置Android权限与前台服务声明

在Android项目的AndroidManifest.xml中添加以下配置,声明前台服务权限和媒体播放服务:

<!-- 添加权限 -->
<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" />

<!-- 在application标签内添加服务声明 -->
<application ...>
    <service android:name=".AudioPlaybackService" android:foregroundServiceType="mediaPlayback" />
</application>

2. 实现Android原生前台服务

在Android项目中创建AudioPlaybackService.cs,用于维持音频播放的前台状态,避免系统回收资源:

using Android.App;
using Android.Content;
using Android.Media;
using Android.OS;
using AndroidX.Core.App;

namespace YourAppNamespace.Droid;

[Service(ForegroundServiceType = Android.Content.PM.ForegroundService.TypeMediaPlayback)]
public class AudioPlaybackService : Service, AudioManager.IOnAudioFocusChangeListener
{
    private AudioManager _audioManager;
    private const int NotificationId = 1001;

    public override void OnCreate()
    {
        base.OnCreate();
        _audioManager = (AudioManager)GetSystemService(AudioService);
    }

    public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
    {
        // 请求音频焦点,确保播放优先级
        var focusResult = _audioManager.RequestAudioFocus(this, Stream.Music, AudioFocus.Gain);
        if (focusResult != AudioFocusRequest.Granted)
        {
            StopSelf();
            return StartCommandResult.NotSticky;
        }

        // 创建前台通知(Android 8.0+必须)
        var notificationIntent = new Intent(this, typeof(MainActivity));
        var pendingIntent = PendingIntent.GetActivity(this, 0, notificationIntent, PendingIntentFlags.Immutable);

        var notification = new NotificationCompat.Builder(this, "audio_playback_channel")
            .SetContentTitle("音频播放中")
            .SetContentText("正在后台播放音频")
            .SetSmallIcon(Resource.Drawable.ic_notification) // 替换为你的通知图标
            .SetContentIntent(pendingIntent)
            .SetPriority(NotificationCompat.PriorityLow)
            .Build();

        // 创建通知渠道
        if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
        {
            var channel = new NotificationChannel("audio_playback_channel", "音频播放", NotificationImportance.Low);
            var notificationManager = (NotificationManager)GetSystemService(NotificationService);
            notificationManager.CreateNotificationChannel(channel);
        }

        // 启动前台服务
        StartForeground(NotificationId, notification);
        return StartCommandResult.Sticky;
    }

    public override void OnDestroy()
    {
        _audioManager.AbandonAudioFocus(this);
        base.OnDestroy();
    }

    public override IBinder OnBind(Intent intent) => null;

    public void OnAudioFocusChange(AudioFocus focusChange)
    {
        // 处理音频焦点变化,比如来电时暂停播放
        if (focusChange == AudioFocus.LossTransient || focusChange == AudioFocus.LossTransientCanDuck)
        {
            // 可在此处调用WebView的JS暂停音频
        }
        else if (focusChange == AudioFocus.Gain)
        {
            // 恢复播放
        }
    }
}

3. MAUI层控制服务启停

在MAUI的音频播放逻辑中,添加平台特定代码来启动/停止前台服务:

using Microsoft.Maui.ApplicationModel;

// 启动后台音频服务
private void StartBackgroundAudioService()
{
#if ANDROID
    var context = Android.App.Application.Context;
    var serviceIntent = new Android.Content.Intent(context, typeof(YourAppNamespace.Droid.AudioPlaybackService));
    
    if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
    {
        context.StartForegroundService(serviceIntent);
    }
    else
    {
        context.StartService(serviceIntent);
    }
#endif
}

// 停止后台音频服务
private void StopBackgroundAudioService()
{
#if ANDROID
    var context = Android.App.Application.Context;
    var serviceIntent = new Android.Content.Intent(context, typeof(YourAppNamespace.Droid.AudioPlaybackService));
    context.StopService(serviceIntent);
#endif
}

调用时机:在音频开始播放时调用StartBackgroundAudioService(),在音频停止或应用退出时调用StopBackgroundAudioService()。

4. 避免WebView被页面跳转销毁

跳转到其他ContentPage时,原页面可能被导航栈销毁,导致WebView回收。可以通过以下两种方式处理:

  • 启用页面缓存:在App.xaml.cs中配置导航页面的缓存模式:
MainPage = new NavigationPage(new MainPage())
{
    NavigationCacheMode = NavigationCacheMode.Enabled
};
  • 保留导航栈中的原页面:跳转时设置animated为true(默认),确保原页面保留在栈中:
await Navigation.PushAsync(new TargetContentPage(), true);

5. 优化WebView的音频播放属性

修改HTML中的audio标签,添加playsinline属性确保Android端播放兼容性,并在WebView加载完成后手动触发播放(Android限制自动播放需要用户交互):

<audio controls autoplay playsinline webkit-playsinline style='width:90%;' id='audioPlayer'>
    <source src='{audioUrl}' type='audio/mpeg'>
    Your browser does not support the audio element.
</audio>

在WebView的Navigated事件中手动调用播放:

private void OnWebViewNavigated(object sender, WebNavigatedEventArgs e)
{
    _ = audio_webview.EvaluateJavaScriptAsync("document.getElementById('audioPlayer').play();");
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 06:04:57