如果应用程序处于后台模式,如何播放声音

how to play sound if app is in background mode

我在 Xamarin.Forms 中开发了 iOS 应用程序。如何在应用程序处于最小化模式时播放声音?

有人可以给出任何提示或示例吗?

提前致谢。

维维克

class musicPlayer {
    public static var instance = musicPlayer()
    var player = AVPlayer()

    func initPlayer(url : String) {
        guard let url = URL.init(string: url) else { return }
        let playerItem = AVPlayerItem.init(url: url)
        player = AVPlayer.init(playerItem: playerItem)
        playAudioBackground()
    }

    func playAudioBackground() {
        do {
            try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, mode: AVAudioSessionModeDefault, options: [.mixWithOthers, .allowAirPlay])
            print("Playback OK")
            try AVAudioSession.sharedInstance().setActive(true)
            print("Session is Active")
        } catch {
            print(error)
        }
    }

    func pause(){
        player.pause()
    }

    func play() {
        player.play()
    }
}

Xamarin iOS中需要先在项目的Capabilities中设置开启info.plist后台模式

然后你需要在AppDelegate.cs中添加以下代码:

public bool FinishedLaunching (UIApplication application, NSDictionary launchOptions)
{
    // Override point for customization after application launch.
    // If not required for your application you can safely delete this method

    AVAudioSession session = AVAudioSession.SharedInstance();
    session.SetCategory(AVAudioSessionCategory.Playback);
    session.SetActive(true);

    return true;
}

然后你可以通过AVPlayer创建一个示例音频播放器:

public partial class ViewController : UIViewController
{
    public AVAudioPlayer player;
    public float MusicVolume
    {
        get;
        set;
    } = 0.5f;

    public bool MusicOn
    {
        get;
        set;
    } = true;

    public ViewController (IntPtr handle) : base (handle)
    {
    }

    public override void ViewDidLoad ()
    {
        base.ViewDidLoad ();
        // Perform any additional setup after loading the view, typically from a nib.
    }

    public override void DidReceiveMemoryWarning ()
    {
        base.DidReceiveMemoryWarning ();
        // Release any cached data, images, etc that aren't in use.
    }

    partial void UIButtonPlay_TouchUpInside(UIButton sender)
    {
        //throw new NotImplementedException();
        Console.WriteLine("play");
        PlayMusic();
    }


    public void PlayMusic()
    {
        NSUrl songURL;
        if (!MusicOn) return;
        //Song url from your local Resource  
        songURL = new NSUrl("Sounds/Alan_Walker.mp3");
        NSError err;
        player = new AVAudioPlayer(songURL, "Song", out err);
        player.Volume = MusicVolume;
        player.FinishedPlaying += delegate {
            // backgroundMusic.Dispose();  
            player = null;
        };
        //Background Music play  
        player.Play();
    }

    partial void UIButtonStop_TouchUpInside(UIButton sender)
    {
        //throw new NotImplementedException();
        Console.WriteLine("stop");
        player.Stop();
    }

    public override void RemoteControlReceived(UIEvent theEvent)
    {
        base.RemoteControlReceived(theEvent);
        Console.WriteLine("RemoteControlReceived");
        switch (theEvent.Subtype)
        {
            case UIEventSubtype.RemoteControlPlay:
                //play the music
                Console.WriteLine("Remote Play"); player.Play();
                break;

            case UIEventSubtype.RemoteControlPause:
                //pause the music
                Console.WriteLine("Remote Stop"); player.Stop();
                break;

            case UIEventSubtype.RemoteControlNextTrack:
                //play next one 
                break;

            case UIEventSubtype.RemoteControlPreviousTrack:
                //play last one 
                break;
            dafault:
                break;
        }
    }

}

最后,可以在DidEnterBackgroundDidBecomeActive时添加背景控件。从 iOS 13 开始,这些生命周期方法已从 AppDelegate 移至 SceneDelegate.

public class SceneDelegate : UIResponder, IUIWindowSceneDelegate
{

    [Export("window")]
    public UIWindow Window { get; set; }

    [Export("scene:willConnectToSession:options:")]
    public void WillConnect(UIScene scene, UISceneSession session, UISceneConnectionOptions connectionOptions)
    {
        // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
        // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
        // This delegate does not imply the connecting scene or session are new (see UIApplicationDelegate `GetConfiguration` instead).
    }

    [Export("sceneDidDisconnect:")]
    public void DidDisconnect(UIScene scene)
    {
        // Called as the scene is being released by the system.
        // This occurs shortly after the scene enters the background, or when its session is discarded.
        // Release any resources associated with this scene that can be re-created the next time the scene connects.
        // The scene may re-connect later, as its session was not neccessarily discarded (see UIApplicationDelegate `DidDiscardSceneSessions` instead).
    }

    [Export("sceneDidBecomeActive:")]
    public void DidBecomeActive(UIScene scene)
    {
        // Called when the scene has moved from an inactive state to an active state.
        // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
        UIApplication.SharedApplication.EndReceivingRemoteControlEvents();
    }

    [Export("sceneWillResignActive:")]
    public void WillResignActive(UIScene scene)
    {
        // Called when the scene will move from an active state to an inactive state.
        // This may occur due to temporary interruptions (ex. an incoming phone call).
    }

    [Export("sceneWillEnterForeground:")]
    public void WillEnterForeground(UIScene scene)
    {
        // Called as the scene transitions from the background to the foreground.
        // Use this method to undo the changes made on entering the background.
    }

    [Export("sceneDidEnterBackground:")]
    public void DidEnterBackground(UIScene scene)
    {
        // Called as the scene transitions from the foreground to the background.
        // Use this method to save data, release shared resources, and store enough scene-specific state information
        // to restore the scene back to its current state.
        UIApplication.SharedApplication.BeginReceivingRemoteControlEvents();
        SetLockInfo();
    }
    public void SetLockInfo()
    {
        //NSMutableDictionary songInfo = new NSMutableDictionary();
        MPNowPlayingInfo playInfo = new MPNowPlayingInfo();
        //image
        MPMediaItemArtwork albumArt = new MPMediaItemArtwork(new UIImage());
        playInfo.Artwork = albumArt;
        //title
        playInfo.Title = "your song name";
        //singer
        playInfo.Artist = "singer name";
        //rate
        playInfo.PlaybackRate = 1.0;
        //current time
        playInfo.ElapsedPlaybackTime = 0;
        //durtaion
        playInfo.PlaybackDuration = 2.35; // the durtaion of the song
        MPNowPlayingInfoCenter.DefaultCenter.NowPlaying = playInfo;

    }
}

进入后台时,需要将当前音频信息传递给MPNowPlayingInfoCenter