• 【Win 10 应用开发】在后台播放视频


    从 1607 (14393)版本开始,MediaPlayer 类就可以在前台与后台之间无缝播放,你不必再考虑前台与后之间的通信,所以从 14393 开始,你就不需要再用 BackgroundMediaPlayer 类了。无论多媒体资源是前台还是后台播放,都由 MediaPlayer 类负责。与此同时,在 XAML 控件体系中,MediaElement 类也由 MediaPlayerElement 类替代。

    这些内容老周就不多说了,因为 SDK 文档的更新日志上都有说明。不管是音频或是视频都统一到 MediaPlayer 类中,正因为如此,实现后台播放视频就显得很是 Easy 了。

    不废话,下面咱们来个示例来说明说明。

    这个示例简单,老周就不用 Page 了,UI 的根直接来个 Grid 吧。

    <Grid
        x:Class="App32.MainView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:App32">
    
        <Grid.RowDefinitions>
            <RowDefinition Height="auto"/>
            <RowDefinition />
        </Grid.RowDefinitions>
    
        <Button Content="打开视频文件" Margin="10,15" Click="OnOpen"/>
        <MediaPlayerElement Name="me" Grid.Row="1" Margin="8"/>
    
    </Grid>

    由于这不是项目默认生成的 Page ,所以注意(看好上面的 x:Class ,它指向对应的类),与它对应的代码文件中的类必须继承 Grid 类,不是 Page 类,代码类的基类就得与 XAML 文档的根元素匹配。

        partial class MainView : Grid
        {
            public MainView()
            {
                InitializeComponent();
            }
        }

    如果,你觉得有必要密封这个类(不让别人继承财产),就这样修饰。

        sealed partial class MainView : Grid
        {
            public MainView()
            {
                InitializeComponent();
            }
        }

    按钮是干啥用的呢,这样,咱们待会儿运行后,点击按钮来打开一个视频文件,然后在 MediaPlayerElement 控件中播放(显示)。

            private async void OnOpen(object sender, RoutedEventArgs e)
            {
                FileOpenPicker picker = new FileOpenPicker();
                picker.FileTypeFilter.Add(".mp4");
                picker.FileTypeFilter.Add(".mkv");
                picker.FileTypeFilter.Add(".wmv");
    
                StorageFile file = await picker.PickSingleFileAsync();
                if (file == null) return;
    
                MediaSource source = MediaSource.CreateFromStorageFile(file);
                me.Source = source;
    
                // 开始播放
                me.MediaPlayer.Play();
            }

    在文首老周说过,现在 UWP 应用的多媒体播放都统一由 MediaPlayer 大哥负责,因此,MediaPlayerElement 类会公开一个 MediaPlayer 属性,你可以从这个属性获取到关联的 MediaPlayer 类实例(这个类位于 Windows.Media.Playback 命名空间)。

    因此你看到了,在上面最后的代码上,老周调用的 Play 方法是 MediaPlayer 类的,实际上视频是在 MediaPlayer 上管理的。MediaPlayerElement 只负责显示(或者 UI 操作,如暂停等)。

    行了,代码这么搞就OK了,不过要想让应用能在后台继续播放,清单文件上的配置是少不了的。打开清单文件(默认是以清单设计器打开),切换到“功能”选项卡,勾选“背景媒体播放”。这是翻译问题,其实应该叫“后台媒体播放”才对,不管它,知道是啥意思就行了,别纠结这些无关紧要的事情。

    清单文件其实是 XML 文档,所以,如果你觉得直接点两下鼠标没法装逼的话,可以手动写的。

    首先,你要引入一个 v3 版本的 UWP 专用命名空间。

    xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3"

    然后,你才能进行声明,写到 Package / Capabilities 节点下面。

      <Capabilities>
        <Capability Name="internetClient" />
        <uap3:Capability Name="backgroundMediaPlayback" />
      </Capabilities>

    最后,保存一下文件就完事了。

    现在运行,随便找个视频播放,然后把程序最小化,或切换到其他应用,然后再切换回去,你会发现,当程序进入后台后,视频仍然在播放。

    其实,上面的实现还不够完善。你想啊,应用移到后台,UI 是看不见的,成了多余的货了,所以,我们妨考虑,在应用扔到后台时销毁 UI,等应用回到前台时再创建 UI。当然了,我们这个例子,界面比较简单,内存占用基本看不出来区别,大约也就是 0.1 MB 的差异。不过为了演示,我们还是处理一下应用程序的这两个事件:

            public App()
            {
                this.InitializeComponent();
                EnteredBackground += App_EnteredBackground;
                LeavingBackground += App_LeavingBackground;
            }

    当应用发送到后台,会引发 EnteredBackground 事件,当应用回到前台,则发生 LeavingBackground 事件。

    下面我们处理一下。

            private void App_LeavingBackground(object sender, LeavingBackgroundEventArgs e)
            {
                if(Window.Current.Content == null) //这里为什么要判断 null 不 null 呢?
                {
                    CreateUI(); //重建界面
                }
            }
    
            private void App_EnteredBackground(object sender, EnteredBackgroundEventArgs e)
            {
                Window.Current.Content = null;//取消对 UI 实例的引用
                GC.Collect(); //打扫卫生
            }

    进入后台后,把当前窗口的内容设为 null,可以解除对 XAML 对象的引用,然后扛一根大扫把来,调用GC成员打扫一下,清理内存。

    当重新回到前台时,重建界面,这里为啥我要先看看窗口的内容是否为 null 呢?很简单啊,如果不为 null 说明界面创建好了,那就不必要重复创建了,没意义了。为啥会这样,因为有件事你得知道,当启动应用时,会执行 OnLaunched 方法,这个没疑问吧。然后呢,LeavingBackground 事件会发生一次。而我们在很多时候,OnLaunched 方法中已经建好界面了,正因为如此,在处理 LeavingBackground 事件时,一定要判断一下界面是否创建过了,如果已有界面实例,就不必再创建了。

    顺便补一下,CreateUI 方法代码如下。

            void CreateUI()
            {
                MainView mv = Window.Current.Content as MainView;
                if (mv == null)
                    mv = new MainView();
                Window.Current.Content = mv;
            }

    然后在你运行实例后,又出现新问题了。还记得吧?我们在 XAML 中放了一个 MediaPlayerElement 控件,现在应用进入后台后界面会释放,所以 MediaPlayerElement 控件的实例也会被干掉,等到应用再回到前台,MediaPlayerElement 控件已经是新创建的实例了,不是刚才的那个实例了,这就造成:回到应用后,视频不再播放了

    要解决这问题,我们只要想办法让 MediaPlayer 实例的生命周期与应用同步就好了,对,把它变成静态变量(static)。所以,我写了这么个类。

        class MediaPlayerHelper
        {
            static MediaPlayer _player;
    
            public static MediaPlayer CurrentPlayer
            {
                get
                {
                    if (_player == null)
                        _player = new MediaPlayer();
                    return _player;
                }
            }
        }

    这样就可以让它静态了,别忘了,如果 MediaPlayerElement 控件实例是新创建的,会关联地创建一个 MediaPlayer 实例。为了让每个新创建的 MediaPlayerElement 控件实例所关联的 MediaPlayer 实例相同,我们可以调用控件的 SetMediaPlayer 方法。

    回到咱们前面那个 Grid 的子类代码中,在构造函数中加上这么一句:

            public MainView()
            {
                InitializeComponent();
                me.SetMediaPlayer(MediaPlayerHelper.CurrentPlayer);
            }

    这样一来,不管咱们在应用生命周期内创建了多少个 MediaPlayerElement 控件实例,其关联的 MediaPlayer 对象都是我们上面公开的那个 CurrentPlayer 了,这确保了 MediaPlayer 实例的全局唯一性。

    现在再次运行示例,在界面元素销毁后再次回到应用程序,视频仍能继续播放。

    好了,今天的话题聊完了,该用饭了。

  • 相关阅读:
    场景调研
    手机搜狗输入法体验
    1到一个整数之间1的个数
    寻找水王
    成套卖书最大优惠问题
    面向对象--多态
    抽象类和接口
    面向对象思想--继承
    面向对象思想--封装
    变量和参数传递
  • 原文地址:https://www.cnblogs.com/tcjiaan/p/7954837.html
Copyright © 2020-2023  润新知