【问题标题】:Xamarin Override RemoteControlReceived in Dependency InjectionXamarin 在依赖注入中覆盖 RemoteControlReceived
【发布时间】:2021-01-10 04:30:25
【问题描述】:

我试图覆盖 UIApplication 的 RemoteControlRecieved 方法并收到以下错误:

NSInternalInconsistencyException 原因:只能有一个 UIApplication 实例。

我了解这个问题,但不知道如何解决它。我有一个实现 UIApplication 和 IStreaming 接口的 StreamingService 类。我所有的 AVPlayer 功能都在这个类中。 StreamingViewModel 类调用

DependencyService.Get<IStreaming>().Play().

当调用此行时,我收到上述错误。我不确定如何从 StreamingService 或 StreamingViewModel 类覆盖 RemoteControlRecieved。

非常感谢任何有关代码示例的帮助。

下面的类

public class StreamingViewModel : INotifyPropertyChanged
{
    public bool DisplayPlay { get => !isPlaying; }
    public bool DisplayPauseStop { get => isPlaying; }

    // INotifyPropertyChanged implementation
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    bool isPlaying;
    bool IsPlaying
    {
        get => isPlaying;
        set
        {
            isPlaying = value;
            // Notify the property has changed
            OnPropertyChanged("DisplayPlay");
            OnPropertyChanged("DisplayPauseStop");
        }
    }

    public void Play()
    {
        DependencyService.Get<IStreaming>().Play();
        IsPlaying = true;
    }

    public void Pause()
    {
        DependencyService.Get<IStreaming>().Pause();
        IsPlaying = false;
    }

    public void Stop()
    {
        DependencyService.Get<IStreaming>().Stop();
        IsPlaying = false;
    }

}

类 StreamingService

[assembly: Xamarin.Forms.ExportRenderer(typeof(MainPage), typeof(StreamingService))]
[assembly: Dependency(typeof(StreamingService))]

namespace test.iOS
{
    public class StreamingService : PageRenderer, IStreaming
    {
        AVPlayer player;
        bool isPrepared;
        string dataSource = "https://stream.voxx.pro/radio/8260/radio.mp3";


        public override void ViewDidLoad()
        {
            Console.WriteLine("StreamService ViewDidLoad");
            base.ViewDidLoad();


        }

            public StreamingService()
        {
            Console.WriteLine("StreamService Default Constructor");

        }


        public void Play()
        {


            Console.WriteLine("Play");

            if (!isPrepared || player == null)
                player = AVPlayer.FromUrl(NSUrl.FromString(dataSource));


            //Audio player Notification in lock screen  
            MPNowPlayingInfo nowPlayingInfo;
            nowPlayingInfo = new MPNowPlayingInfo();
            nowPlayingInfo.Artist = "Radio Caravan";
            nowPlayingInfo.Title = "Join The Caravan";

            // Register for receiving controls from lock screen and controlscreen  
            MPNowPlayingInfoCenter.DefaultCenter.NowPlaying = nowPlayingInfo;

            //var command = MPRemoteCommandCenter.Shared;
            //command.PlayCommand.Enabled = true;
            //command.PauseCommand.Enabled = true;


            //command.NextTrackCommand.Enabled = false;
            //command.PreviousTrackCommand.Enabled = false;


            isPrepared = true;
            player.Play();

            base.BecomeFirstResponder();

            //To listen changes in lock screen  
            UIApplication.SharedApplication.BeginReceivingRemoteControlEvents();

        }

        public void Pause()
        {
            player.Pause();
        }

        public void Stop()
        {
            player.Dispose();
            isPrepared = false;
        }


        public override void RemoteControlReceived(UIEvent theEvent)
        {
            Console.WriteLine("Remote Control Received");
            base.RemoteControlReceived(theEvent);

            if (theEvent.Subtype == UIEventSubtype.RemoteControlPause)
            {
                Console.WriteLine("Remote Pause");
                player.Pause();
            }
            else if (theEvent.Subtype == UIEventSubtype.RemoteControlPlay)
            {
                Console.WriteLine("Remote Play");
                player.Play();
            }
        }

    }
}

MainPage.cs

    namespace test
{
    public partial class MainPage : ContentPage
    {
        private StreamingViewModel ViewModel { get { return (StreamingViewModel)this.BindingContext; } }

        public MainPage()
        {
            InitializeComponent();
            On<Xamarin.Forms.PlatformConfiguration.iOS>().SetUseSafeArea(true);

            BindingContext = new StreamingViewModel();
        }

        // Callbacks to images tapped
        private void Play_tapped(object sender, EventArgs e)
        {
            ViewModel.Play();
        }

        private void Pause_tapped(object sender, EventArgs e)
        {
            ViewModel.Pause();
        }

        private void Stop_tapped(object sender, EventArgs e)
        {
            ViewModel.Stop();
        }
    }

}

接口:IStreaming

    public interface IStreaming
{
    void Play();
    void Pause();
    void Stop();
    
}

【问题讨论】:

    标签: c# ios xamarin dependency-injection


    【解决方案1】:

    NSInternalInconsistencyException 原因:只能有一个 UIApplication 实例。

    您可以自定义 PageRenderer 来覆盖 RemoteControlReceived 方法来检查它是否有效。因为这将使用现有的UIApplication

    关于isPlaying属性,您可以在CustomPage中使用Bindable Properties来定义。

    关于Play/Pause/Stop 方法,您可以使用MessagingCenter 将消息从Forms 发送到CustomPageRenderer

    例如CustomPage的部分代码如下:

    public partial class CustomPage: ContentPage
    {
        public CustomPage()
        {
            InitializeComponent();
        }
    
        public static readonly BindableProperty IsPlayingProperty = BindableProperty.Create("IsPlaying", typeof(bool), typeof(CustomPage), null);
    
        public bool IsPlaying
        {
            get { return (bool)GetValue(IsPlayingProperty); }
            set { SetValue(IsPlayingProperty, value); }
        }
    
        public void Play()
        {
            MessagingCenter.Send<object, string>(this, "PlayControl", "Play");
            IsPlaying = true;
        }
    
        public void Pause()
        {
            MessagingCenter.Send<object, string>(this, "PlayControl", "Pause");
            IsPlaying = false;
        }
    
        public void Stop()
        {
            MessagingCenter.Send<object, string>(this, "PlayControl", "Stop");
            IsPlaying = false;
        }
    
    }
    

    CustomPageRenderer部分代码如下:

    [assembly: Xamarin.Forms.ExportRenderer(typeof(CustomPage), typeof(CustomPageRenderer))]
    namespace XamarinForms20.iOS
    {
        public class CustomPageRenderer : PageRenderer
        {
             
            public override void ViewDidLoad()
            {
                base.ViewDidLoad();
    
                //...
    
                MessagingCenter.Subscribe<object, string>(this, "PlayControl",  (sender, arg) =>
                {
                    if(arg == "Play")
                    {
                        //...
                    }else if (arg == "Pause")
                    {
                        //...
                    }else if (arg == "Stop")
                    {
                        //...
                    }
                });
            }
    
            public override void RemoteControlReceived(UIEvent theEvent)
            {
                base.RemoteControlReceived(theEvent);
    
                if (theEvent.Subtype == UIEventSubtype.RemoteControlPlay)
                    player.Play();
                else if (theEvent.Subtype == UIEventSubtype.RemoteControlPause)
                    player.Pause();
            }
        }
    }
    

    =====================================更新=========== ======================

    您可以尝试在 AVAudioSession.InterruptionNotificationViewDidload 方法中添加 NSNotificationCenter

    public override void ViewDidLoad()
    ...
    NSObject _notificationHandle = NSNotificationCenter.DefaultCenter.AddObserver(AVAudioSession.InterruptionNotification, HandleNotification);
    ...
    
    private void HandleNotification(NSNotification notification)
    {
        // Do something
        NSDictionary userinfo = notification.UserInfo;
        NSNumber number = (NSNumber)userinfo.ValueForKey(new NSString("AVAudioSessionInterruptionTypeKey"));
        int value = (int)number.NIntValue;
    
        if(value == 1)
        {
            // pause
        }else if(value == 0)
        {
            // countinue to play
            AVAudioSession.SharedInstance().SetActive(true);
        }
    }
    

    【讨论】:

    • 感谢您的回复,我已尝试通过实现允许 RemoteControlReceived 运行的 PageRenderer 对我的 StreamingService 类进行一些更改,但播放器似乎每次都为空,知道如何修复那?下面是我的课:
    • 我已通过更改更新了问题。
    • @SameerJaffer 你好,试试用AVPlayer player = new AVPlayer(new NSUrl(""));生成AVPlayer的实例,看看它是否为null。
    • 发生的事情是 StreamService 类被实例化了两次,一次在 PageRenderer 上,然后当 StreamViewModel 运行 DependencyService.Get().Play();来自 Play 方法。我尝试了您的上述行,但它不会暂停或播放。似乎在每次播放/暂停/停止图像单击时,它都在实例化一个新的 StreamService 类。
    • 输出:2021-01-18 10:52:22.019 JoinTheCaravan.iOS[15230:5923982] StreamService 默认构造函数 2021-01-18 10:52:22.020 JoinTheCaravan.iOS[15230:5923982] StreamService ViewDidLoad 2021-01-18 10:52:24.218 JoinTheCaravan.iOS[15230:5923982] StreamingViewModel - Play 2021-01-18 10:52:24.224 JoinTheCaravan.iOS[15230:5923982] StreamService 默认构造函数 2021-01-18 10: 52:24.226 JoinTheCaravan.iOS[15230:5923982] 开始播放线程:#7
    猜你喜欢
    • 1970-01-01
    • 2021-09-08
    • 1970-01-01
    • 2017-09-08
    • 1970-01-01
    • 1970-01-01
    • 2015-07-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多