Windows phone 8 学习笔记(6) 多任务(转)
Windows phone 8 是一个单任务操作系统,任何时候都只有一个应用处于活跃状态,这里的多任务是指对后台任务的支持。本节我们先讲讲应用程序的运行状态,然后看看支持的后台任务,包括:后台代理、后台音频、后台文件传输、后台辅助线程等。
快速导航:
一、应用的状态
二、后台代理
三、后台音频
四、后台文件传输
五、后台辅助线程
一、应用的状态
1)应用的运行状态
我们通过图解来分析应用的运行状态,启动并置于前台界面的应用是唯一处于运行状态的,其他的操作,比如win键,后退导出应用,打开选择器和启动器时都会让当前运行的应用进入休眠状态,如果系统内存不足,处于休眠状态的应用可能会被系统逻辑删除。下面的图示演示了这个过程。
2)如何恢复状态
当应用处于休眠状态时,它的状态信息仍然保留在内存中,用户下次切换进去后不会有任何变化。但是当应用被逻辑删除后,这些状态信息就会丢失,比如表单填写的内容都会消失,为了避免这种情况,我们需要手动保留状态信息。
首先,我们在mainpage定义一些页面表单控件:
[XAML]
- <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
- <TextBox Text="{Binding TextBox1Text, Mode=TwoWay}" Height="72"
- HorizontalAlignment="Left" Margin="20,20,0,0" Name="textBox1"
- VerticalAlignment="Top" Width="440" />
- <CheckBox IsChecked="{Binding CheckBox1IsChecked, Mode=TwoWay}"
- Content="CheckBox" Height="71" Name="checkBox1" Margin="20,100,0,0"
- VerticalAlignment="Top"/>
- <Slider Value="{Binding Slider1Value, Mode=TwoWay}" Height="84" Name="slider1"
- Width="440" Margin="20,180,0,0" VerticalAlignment="Top"/>
- <RadioButton IsChecked="{Binding RadioButton1IsChecked, Mode=TwoWay}"
- Content="RadioButton 1" Height="71" Name="radioButton1"
- GroupName="RadioButtonGroup" Margin="20,260,0,0" VerticalAlignment="Top"/>
- <RadioButton IsChecked="{Binding RadioButton1IsChecked, Mode=TwoWay}"
- Content="RadioButton 2" Height="71" Name="radioButton2"
- GroupName="RadioButtonGroup" Margin="20,340,0,0" VerticalAlignment="Top"/>
- </Grid>
我们需要实现在应用逻辑删除后能将其状态保持到页面的State字典中,但是需要我们的数据源支持序列化,所以我们定义与表单关联的ViewModel如下:
[C#]
- [DataContract]
- public class ViewModel : INotifyPropertyChanged
- {
- private string _textBox1Text;
- private bool _checkBox1IsChecked;
- private bool _radioButton1IsChecked;
- private bool _radioButton2IsChecked;
- private double _slider1Value;
- [DataMember]
- public string TextBox1Text
- {
- get { return _textBox1Text; }
- set
- {
- _textBox1Text = value;
- NotifyPropertyChanged("TextBox1Text");
- }
- }
- [DataMember]
- public bool CheckBox1IsChecked
- {
- get { return _checkBox1IsChecked; }
- set
- {
- _checkBox1IsChecked = value;
- NotifyPropertyChanged("CheckBox1IsChecked");
- }
- }
- [DataMember]
- public double Slider1Value
- {
- get { return _slider1Value; }
- set
- {
- _slider1Value = value;
- NotifyPropertyChanged("Slider1Value");
- }
- }
- [DataMember]
- public bool RadioButton1IsChecked
- {
- get { return _radioButton1IsChecked; }
- set
- {
- _radioButton1IsChecked = value;
- NotifyPropertyChanged("RadioButton1IsChecked");
- }
- }
- [DataMember]
- public bool RadioButton2IsChecked
- {
- get { return _radioButton2IsChecked; }
- set
- {
- _radioButton2IsChecked = value;
- NotifyPropertyChanged("RadioButton2IsChecked");
- }
- }
- public event PropertyChangedEventHandler PropertyChanged;
- private void NotifyPropertyChanged(string propertyName)
- {
- if (null != PropertyChanged)
- PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
- }
- }
我需要对mainpage代码添加页面导航入、导航出的事件。导航出页面的时候,如果不是向后导航,则存储状态。导航入的时候,我们需要判断页面是否为逻辑删除后正在恢复的状态,如果是,则通过状态字典恢复状态。mainpage代码如下:
[C#]
- public partial class MainPage : PhoneApplicationPage
- {
- // 构造函数
- public MainPage()
- {
- InitializeComponent();
- _isNewPageInstance = true;
- }
- ViewModel _viewModel = null;
- /// <summary>
- /// 新实例还是现有实例
- /// </summary>
- bool _isNewPageInstance = false;
- private void Button_Click_1(object sender, RoutedEventArgs e)
- {
- NavigationService.Navigate(new Uri("/Page1.xaml", UriKind.Relative));
- }
- protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
- {
- //如果不是向后导航,则保存状态
- if (e.NavigationMode != System.Windows.Navigation.NavigationMode.Back)
- {
- State["ViewModel"] = _viewModel;
- }
- }
- protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
- {
- if (_isNewPageInstance)
- {
- if (_viewModel == null)
- {
- if (State.Count > 0)
- {
- _viewModel = (ViewModel)State["ViewModel"];
- }
- else
- {
- _viewModel = new ViewModel();
- }
- }
- DataContext = _viewModel;
- }
- _isNewPageInstance = false;
- }
- }
然后我们添加一page1页面,该页添加一个返回按钮。用于测试。为了达到调试时即时进入逻辑删除的效果,我们需要设置下。右键项目文件,点属性,在调试选项卡勾选“在调试期间取消激活时逻辑删除”。
二、后台代理
后台代理可以在应用退出以后独立在系统后台运行,它包含两种类型的代理,分别是定期代理和资源密集型代理,前者用于频繁执行小任务,后者用于在系统空闲时执行耗时大任务。要使用后台代理,我们需要添加一个名为Windows phone 计划任务代理的项目,并在应用的项目中添加对其的引用,现在我们要实现在后台代理中弹出Toast,我们需要如下修改ScheduledAgent.cs的OnInvoke方法,代码如下
[C#]
- protected override void OnInvoke(ScheduledTask task)
- {
- string toastMessage = "";
- if (task is PeriodicTask)
- {
- toastMessage = "定期代理正在运行";
- }
- else
- {
- toastMessage = "资源密集型代理正在运行";
- }
- // 用于向用户显示Toast,如果当前任务的前台正在运行,则不显示
- ShellToast toast = new ShellToast();
- toast.Title = "标题";
- toast.Content = toastMessage;
- toast.Show();
- // 在调试的时候需要及时执行查看效果
- #if DEBUG_AGENT
- ScheduledActionService.LaunchForTest(task.Name, TimeSpan.FromSeconds(15));
- #endif
- NotifyComplete();
- }
接着,我们在应用项目的mainpage中调用代理,代码如下:
[XAML]
- <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
- <StackPanel>
- <StackPanel Orientation="Vertical" Name="PeriodicStackPanel" Margin="0,0,0,40">
- <TextBlock Text="定期代理" Style="{StaticResource PhoneTextTitle2Style}"/>
- <StackPanel Orientation="Horizontal">
- <TextBlock Text="名称: " Style="{StaticResource PhoneTextAccentStyle}"/>
- <TextBlock Text="{Binding Name}" />
- </StackPanel>
- <StackPanel Orientation="Horizontal">
- <TextBlock Text="是否可用" VerticalAlignment="Center" Style="{StaticResource PhoneTextAccentStyle}"/>
- <CheckBox Name="PeriodicCheckBox" IsChecked="{Binding IsEnabled}" Checked="PeriodicCheckBox_Checked" Unchecked="PeriodicCheckBox_Unchecked"/>
- </StackPanel>
- <StackPanel Orientation="Horizontal">
- <TextBlock Text="是否已计划: " Style="{StaticResource PhoneTextAccentStyle}"/>
- <TextBlock Text="{Binding IsScheduled}" />
- </StackPanel>
- <StackPanel Orientation="Horizontal">
- <TextBlock Text="上次计划运行时间: " Style="{StaticResource PhoneTextAccentStyle}"/>
- <TextBlock Text="{Binding LastScheduledTime}" />
- </StackPanel>
- <StackPanel Orientation="Horizontal">
- <TextBlock Text="计划结束时间: " Style="{StaticResource PhoneTextAccentStyle}"/>
- <TextBlock Text="{Binding ExpirationTime}" />
- </StackPanel>
- <StackPanel Orientation="Horizontal">
- <TextBlock Text="上一次代理运行退出的原因: " Style="{StaticResource PhoneTextAccentStyle}"/>
- <TextBlock Text="{Binding LastExitReason}" />
- </StackPanel>
- </StackPanel>
- <StackPanel Orientation="Vertical" Name="ResourceIntensiveStackPanel" Margin="0,0,0,40">
- <TextBlock Text="资源密集型代理" Style="{StaticResource PhoneTextTitle2Style}"/>
- <StackPanel Orientation="Horizontal">
- <TextBlock Text="名称: " Style="{StaticResource PhoneTextAccentStyle}"/>
- <TextBlock Text="{Binding Name}" />
- </StackPanel>
- <StackPanel Orientation="Horizontal">
- <TextBlock Text="是否可用" VerticalAlignment="Center" Style="{StaticResource PhoneTextAccentStyle}"/>
- <CheckBox Name="ResourceIntensiveCheckBox" IsChecked="{Binding IsEnabled}" Checked="ResourceIntensiveCheckBox_Checked" Unchecked="ResourceIntensiveCheckBox_Unchecked"/>
- </StackPanel>
- <StackPanel Orientation="Horizontal">
- <TextBlock Text="是否已计划: " Style="{StaticResource PhoneTextAccentStyle}"/>
- <TextBlock Text="{Binding IsScheduled}" />
- </StackPanel>
- <StackPanel Orientation="Horizontal">
- <TextBlock Text="上次计划运行时间: " Style="{StaticResource PhoneTextAccentStyle}"/>
- <TextBlock Text="{Binding LastScheduledTime}" />
- </StackPanel>
- <StackPanel Orientation="Horizontal">
- <TextBlock Text="计划结束时间: " Style="{StaticResource PhoneTextAccentStyle}"/>
- <TextBlock Text="{Binding ExpirationTime}" />
- </StackPanel>
- <StackPanel Orientation="Horizontal">
- <TextBlock Text="上一次代理运行退出的原因: " Style="{StaticResource PhoneTextAccentStyle}"/>
- <TextBlock Text="{Binding LastExitReason}" />
- </StackPanel>
- </StackPanel>
- </StackPanel>
- </Grid>
[C#]
- public partial class MainPage : PhoneApplicationPage
- {
- /// <summary>
- /// 定期代理
- /// </summary>
- PeriodicTask periodicTask;
- /// <summary>
- /// 资源密集型代理
- /// </summary>
- ResourceIntensiveTask resourceIntensiveTask;
- string periodicTaskName = "PeriodicAgent";
- string resourceIntensiveTaskName = "ResourceIntensiveAgent";
- public bool agentsAreEnabled = true;
- // 构造函数
- public MainPage()
- {
- InitializeComponent();
- }
- //启动定期代理
- private void StartPeriodicAgent()
- {
- agentsAreEnabled = true;
- // 获取当前名称的定期代理,如果存在则移除
- periodicTask = ScheduledActionService.Find(periodicTaskName) as PeriodicTask;
- if (periodicTask != null)
- {
- RemoveAgent(periodicTaskName);
- }
- periodicTask = new PeriodicTask(periodicTaskName);
- periodicTask.Description = "这是一个定期代理的描述信息。";
- try
- {
- ScheduledActionService.Add(periodicTask);
- PeriodicStackPanel.DataContext = periodicTask;
- //在调试的时候需要及时执行查看效果
- #if(DEBUG_AGENT)
- ScheduledActionService.LaunchForTest(periodicTaskName, TimeSpan.FromSeconds(60));
- #endif
- }
- catch (InvalidOperationException exception)
- {
- if (exception.Message.Contains("BNS Error: The action is disabled"))
- {
- MessageBox.Show("本应用的后台计划被用户禁用。");
- agentsAreEnabled = false;
- PeriodicCheckBox.IsChecked = false;
- }
- if (exception.Message.Contains("BNS Error: The maximum number of ScheduledActions of this type have already been added."))
- {
- MessageBox.Show("定期代理数量达到最大限制。");
- }
- PeriodicCheckBox.IsChecked = false;
- }
- catch (SchedulerServiceException)
- {
- PeriodicCheckBox.IsChecked = false;
- }
- }
- private void StartResourceIntensiveAgent()
- {
- agentsAreEnabled = true;
- // 获取当前名称的资源密集型代理,如果存在则移除
- resourceIntensiveTask = ScheduledActionService.Find(resourceIntensiveTaskName) as ResourceIntensiveTask;
- if (resourceIntensiveTask != null)
- {
- RemoveAgent(resourceIntensiveTaskName);
- }
- resourceIntensiveTask = new ResourceIntensiveTask(resourceIntensiveTaskName);
- resourceIntensiveTask.Description = "这是一个资源密集型代理的描述信息。";
- try
- {
- ScheduledActionService.Add(resourceIntensiveTask);
- ResourceIntensiveStackPanel.DataContext = resourceIntensiveTask;
- //在调试的时候需要及时执行查看效果
- #if(DEBUG_AGENT)
- ScheduledActionService.LaunchForTest(resourceIntensiveTaskName, TimeSpan.FromSeconds(15));
- #endif
- }
- catch (InvalidOperationException exception)
- {
- if (exception.Message.Contains("BNS Error: The action is disabled"))
- {
- MessageBox.Show("本应用的后台计划被用户禁用。");
- agentsAreEnabled = false;
- }
- ResourceIntensiveCheckBox.IsChecked = false;
- }
- catch (SchedulerServiceException)
- {
- ResourceIntensiveCheckBox.IsChecked = false;
- }
- }
- bool ignoreCheckBoxEvents = false;
- private void PeriodicCheckBox_Checked(object sender, RoutedEventArgs e)
- {
- if (ignoreCheckBoxEvents)
- return;
- StartPeriodicAgent();
- }
- private void PeriodicCheckBox_Unchecked(object sender, RoutedEventArgs e)
- {
- if (ignoreCheckBoxEvents)
- return;
- RemoveAgent(periodicTaskName);
- }
- private void ResourceIntensiveCheckBox_Checked(object sender, RoutedEventArgs e)
- {
- if (ignoreCheckBoxEvents)
- return;
- StartResourceIntensiveAgent();
- }
- private void ResourceIntensiveCheckBox_Unchecked(object sender, RoutedEventArgs e)
- {
- if (ignoreCheckBoxEvents)
- return;
- RemoveAgent(resourceIntensiveTaskName);
- }
- /// <summary>
- /// 删除代理
- /// </summary>
- private void RemoveAgent(string name)
- {
- try
- {
- ScheduledActionService.Remove(name);
- }
- catch (Exception)
- {
- }
- }
- protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
- {
- ignoreCheckBoxEvents = true;
- periodicTask = ScheduledActionService.Find(periodicTaskName) as PeriodicTask;
- if (periodicTask != null)
- {
- PeriodicStackPanel.DataContext = periodicTask;
- }
- resourceIntensiveTask = ScheduledActionService.Find(resourceIntensiveTaskName) as ResourceIntensiveTask;
- if (resourceIntensiveTask != null)
- {
- ResourceIntensiveStackPanel.DataContext = resourceIntensiveTask;
- }
- ignoreCheckBoxEvents = false;
- }
- }
三、后台音频
通过后台音频的功能我们可以实现在系统后台播放音乐的功能,由于后台音频代理只能访问本地文件夹,所以我们务必要先把需要播放的音乐文件拷贝到本地文件夹中。本示例是把安装文件夹的音频文件拷贝到本地文件夹,代码如下:
[C#]
- //把安装文件夹下的文件拷贝到本地文件夹
- private void CopyToIsolatedStorage()
- {
- using (IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication())
- {
- string[] files = new string[] { "Ring01.wma", "Ring02.wma", "Ring03.wma" };
- foreach (var _fileName in files)
- {
- if (!storage.FileExists(_fileName))
- {
- string _filePath = "Audio/" + _fileName;
- StreamResourceInfo resource = Application.GetResourceStream(new Uri(_filePath, UriKind.Relative));
- using (IsolatedStorageFileStream file = storage.CreateFile(_fileName))
- {
- int chunkSize = 4096;
- byte[] bytes = new byte[chunkSize];
- int byteCount;
- while ((byteCount = resource.Stream.Read(bytes, 0, chunkSize)) > 0)
- {
- file.Write(bytes, 0, byteCount);
- }
- }
- }
- }
- string[] icons = new string[] { "Ring01.jpg", "Ring02.jpg", "Ring03.jpg" };
- foreach (var _fileName in icons)
- {
- if (!storage.FileExists(_fileName))
- {
- string _filePath = "Images/" + _fileName;
- StreamResourceInfo iconResource = Application.GetResourceStream(new Uri(_filePath, UriKind.Relative));
- using (IsolatedStorageFileStream file = storage.CreateFile( _fileName))
- {
- int chunkSize = 4096;
- byte[] bytes = new byte[chunkSize];
- int byteCount;
- while ((byteCount = iconResource.Stream.Read(bytes, 0, chunkSize)) > 0)
- {
- file.Write(bytes, 0, byteCount);
- }
- }
- }
- }
- }
- }
我们需要在解决方案中添加Windows phone 音频播放代理项目,并在应用项目中添加对其的引用。修改AudioPlayer.cs代码如下:
[C#]
- public class AudioPlayer : AudioPlayerAgent
- {
- private static volatile bool _classInitialized;
- private static List<AudioTrack> _playList = new List<AudioTrack>
- {
- new AudioTrack(new Uri("Ring01.wma", UriKind.Relative),"曲目1","艺术家1","专辑1",new Uri("Ring01.jpg", UriKind.Relative)),
- new AudioTrack(new Uri("Ring02.wma", UriKind.Relative),"曲目2","艺术家2","专辑2",new Uri("Ring02.jpg", UriKind.Relative)),
- new AudioTrack(new Uri("Ring03.wma", UriKind.Relative),"曲目3","艺术家3","专辑3",new Uri("Ring03.jpg", UriKind.Relative))
- };
- /// <summary>
- /// 当前播放位置
- /// </summary>
- static int currentTrackNumber = 0;
- /// <remarks>
- /// AudioPlayer 实例可共享同一进程。
- /// 静态字段可用于在 AudioPlayer 实例之间共享状态
- /// 或与音频流代理通信。
- /// </remarks>
- public AudioPlayer()
- {
- if (!_classInitialized)
- {
- _classInitialized = true;
- // 订阅托管异常处理程序
- Deployment.Current.Dispatcher.BeginInvoke(delegate
- {
- Application.Current.UnhandledException += AudioPlayer_UnhandledException;
- });
- }
- }
- /// 出现未处理的异常时执行的代码
- private void AudioPlayer_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
- {
- if (System.Diagnostics.Debugger.IsAttached)
- {
- // 出现未处理的异常;强行进入调试器
- System.Diagnostics.Debugger.Break();
- }
- }
- /// <summary>
- /// playstate 更改时调用,但 Error 状态除外(参见 OnError)
- /// </summary>
- /// <param name="player">BackgroundAudioPlayer</param>
- /// <param name="track">在 playstate 更改时播放的曲目</param>
- /// <param name="playState">播放机的新 playstate </param>
- /// <remarks>
- /// 无法取消播放状态更改。即使应用程序
- /// 导致状态自行更改,假定应用程序已经选择了回调。
- ///
- /// 值得注意的 playstate 事件
- /// (a) TrackEnded: 播放器没有当前曲目时激活。代理可设置下一曲目。
- /// (b) TrackReady: 音轨已设置完毕,现在可以播放。
- ///
- /// 只在代理请求完成之后调用一次 NotifyComplete(),包括异步回调。
- /// </remarks>
- protected override void OnPlayStateChanged(BackgroundAudioPlayer player, AudioTrack track, PlayState playState)
- {
- switch (playState)
- {
- case PlayState.TrackEnded:
- player.Track = GetPreviousTrack();
- break;
- case PlayState.TrackReady:
- player.Play();
- break;
- case PlayState.Shutdown:
- // TODO: 在此处理关机状态(例如保存状态)
- break;
- case PlayState.Unknown:
- break;
- case PlayState.Stopped:
- break;
- case PlayState.Paused:
- break;
- case PlayState.Playing:
- break;
- case PlayState.BufferingStarted:
- break;
- case PlayState.BufferingStopped:
- break;
- case PlayState.Rewinding:
- break;
- case PlayState.FastForwarding:
- break;
- }
- NotifyComplete();
- }
- /// <summary>
- /// 在用户使用应用程序/系统提供的用户界面请求操作时调用
- /// </summary>
- /// <param name="player">BackgroundAudioPlayer</param>
- /// <param name="track">用户操作期间播放的曲目</param>
- /// <param name="action">用户请求的操作</param>
- /// <param name="param">与请求的操作相关联的数据。
- /// 在当前版本中,此参数仅适合与 Seek 操作一起使用,
- /// 以指明请求的乐曲的位置</param>
- /// <remarks>
- /// 用户操作不自动对系统状态进行任何更改;如果用户操作受支持,
- /// 执行用户操作(如果这些操作受支持)。
- ///
- /// 只在代理请求完成之后调用一次 NotifyComplete(),包括异步回调。
- /// </remarks>
- protected override void OnUserAction(BackgroundAudioPlayer player, AudioTrack track, UserAction action, object param)
- {
- switch (action)
- {
- case UserAction.Play:
- if (player.PlayerState != PlayState.Playing)
- {
- player.Play();
- }
- break;
- case UserAction.Stop:
- player.Stop();
- break;
- case UserAction.Pause:
- player.Pause();
- break;
- case UserAction.FastForward:
- player.FastForward();
- break;
- case UserAction.Rewind:
- player.Rewind();
- break;
- case UserAction.Seek:
- player.Position = (TimeSpan)param;
- break;
- case UserAction.SkipNext:
- player.Track = GetNextTrack();
- break;
- case UserAction.SkipPrevious:
- AudioTrack previousTrack = GetPreviousTrack();
- if (previousTrack != null)
- {
- player.Track = previousTrack;
- }
- break;
- }
- NotifyComplete();
- }
- /// <summary>
- /// 实现逻辑以获取下一个 AudioTrack 实例。
- /// 在播放列表中,源可以是文件、Web 请求,等等。
- /// </summary>
- /// <remarks>
- /// AudioTrack URI 确定源,它可以是:
- /// (a) 独立存储器文件(相对 URI,表示独立存储器中的路径)
- /// (b) HTTP URL(绝对 URI)
- /// (c) MediaStreamSource (null)
- /// </remarks>
- /// <returns>AudioTrack 实例,或如果播放完毕,则返回 null</returns>
- private AudioTrack GetNextTrack()
- {
- // TODO: 添加逻辑以获取下一条音轨
- if (++currentTrackNumber >= _playList.Count) currentTrackNumber = 0;
- AudioTrack track = _playList[currentTrackNumber];
- // 指定曲目
- return track;
- }
- /// <summary>
- /// 实现逻辑以获取前一个 AudioTrack 实例。
- /// </summary>
- /// <remarks>
- /// AudioTrack URI 确定源,它可以是:
- /// (a) 独立存储器文件(相对 URI,表示独立存储器中的路径)
- /// (b) HTTP URL(绝对 URI)
- /// (c) MediaStreamSource (null)
- /// </remarks>
- /// <returns>AudioTrack 实例,或如果不允许前一曲目,则返回 null</returns>
- private AudioTrack GetPreviousTrack()
- {
- // TODO: 添加逻辑以获取前一条音轨
- if (--currentTrackNumber < 0) currentTrackNumber = _playList.Count - 1;
- AudioTrack track = _playList[currentTrackNumber];
- // 指定曲目
- return track;
- }
- /// <summary>
- /// 每次播放出错(如 AudioTrack 未正确下载)时调用
- /// </summary>
- /// <param name="player">BackgroundAudioPlayer</param>
- /// <param name="track">出现错误的曲目</param>
- /// <param name="error">出现的错误</param>
- /// <param name="isFatal">如果为 true,则播放不能继续并且曲目播放将停止</param>
- /// <remarks>
- /// 不保证在所有情况下都调用此方法。例如,如果后台代理程序
- /// 本身具有未处理的异常,则不会回调它来处理它自己的错误。
- /// </remarks>
- protected override void OnError(BackgroundAudioPlayer player, AudioTrack track, Exception error, bool isFatal)
- {
- if (isFatal)
- {
- Abort();
- }
- else
- {
- NotifyComplete();
- }
- }
- /// <summary>
- /// 取消代理请求时调用
- /// </summary>
- /// <remarks>
- /// 取消请求后,代理需要 5 秒钟完成其工作,
- /// 通过调用 NotifyComplete()/Abort()。
- /// </remarks>
- protected override void OnCancel()
- {
- }
- }
最后,我们在mainpage中添加对播放的控制。
[XAML]
- <Grid x:Name="LayoutRoot" Background="Transparent">
- <Grid.RowDefinitions>
- <RowDefinition Height="Auto"/>
- <RowDefinition Height="*"/>
- </Grid.RowDefinitions>
- <!--TitlePanel 包含应用程序的名称和页标题-->
- <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
- <TextBlock x:Name="ApplicationTitle" Text="后台音频" Style="{StaticResource PhoneTextNormalStyle}"/>
- </StackPanel>
- <!--ContentPanel - 在此处放置其他内容-->
- <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
- <Button x:Name="button2" Content="〈" HorizontalAlignment="Left" Margin="13,10,0,0" VerticalAlignment="Top" Click="Button_Click_2"/>
- <Button x:Name="button1" Content="▶" HorizontalAlignment="Left" Margin="75,10,0,0" VerticalAlignment="Top" Click="Button_Click_1"/>
- <Button x:Name="button3" Content="〉" HorizontalAlignment="Left" Margin="136,10,0,0" VerticalAlignment="Top" Click="Button_Click_3" />
- <TextBlock x:Name="textblock1" HorizontalAlignment="Left" Margin="22,87,0,0" TextWrapping="Wrap" VerticalAlignment="Top"/>
- <Image x:Name="imge1" HorizontalAlignment="Left" Height="100" Margin="22,142,0,0" VerticalAlignment="Top" Width="100"/>
- </Grid>
- </Grid>
[C#]
- public partial class MainPage : PhoneApplicationPage
- {
- // 构造函数
- public MainPage()
- {
- InitializeComponent();
- BackgroundAudioPlayer.Instance.PlayStateChanged += new EventHandler(Instance_PlayStateChanged);
- }
- //刚加载时确定播放状态
- protected override void OnNavigatedTo(NavigationEventArgs e)
- {
- if (PlayState.Playing == BackgroundAudioPlayer.Instance.PlayerState)
- {
- button1.Content = "■";
- textblock1.Text = "曲目:" + BackgroundAudioPlayer.Instance.Track.Title
- + " 艺术家:" + BackgroundAudioPlayer.Instance.Track.Artist
- + " 专辑:" + BackgroundAudioPlayer.Instance.Track.Album
- + " 曲目长度:" +BackgroundAudioPlayer.Instance.Track.Duration.Minutes + ":" + BackgroundAudioPlayer.Instance.Track.Duration.Seconds;
- using (IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication())
- {
- var stream = storage.OpenFile(BackgroundAudioPlayer.Instance.Track.AlbumArt.OriginalString, System.IO.FileMode.Open);
- var bitmapImage = new BitmapImage();
- bitmapImage.SetSource(stream);
- imge1.Source = bitmapImage;
- stream.Close();
- }
- }
- else
- {
- button1.Content = "▶";
- textblock1.Text = "未播放曲目";
- }
- }
- void Instance_PlayStateChanged(object sender, EventArgs e)
- {
- switch (BackgroundAudioPlayer.Instance.PlayerState)
- {
- case PlayState.Playing:
- button1.Content = "■";
- button2.IsEnabled = true;
- button3.IsEnabled = true;
- break;
- case PlayState.Paused:
- case PlayState.Stopped:
- button1.Content = "▶";
- break;
- }
- if (null != BackgroundAudioPlayer.Instance.Track && BackgroundAudioPlayer.Instance.PlayerState!= PlayState.Stopped)
- {
- textblock1.Text = "曲目:" + BackgroundAudioPlayer.Instance.Track.Title
- + " 艺术家:" + BackgroundAudioPlayer.Instance.Track.Artist
- + " 专辑:" + BackgroundAudioPlayer.Instance.Track.Album
- + " 曲目长度:" + BackgroundAudioPlayer.Instance.Track.Duration.Minutes + ":" + BackgroundAudioPlayer.Instance.Track.Duration.Seconds;
- using (IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication())
- {
- var stream = storage.OpenFile(BackgroundAudioPlayer.Instance.Track.AlbumArt.OriginalString, System.IO.FileMode.Open);
- var bitmapImage = new BitmapImage();
- bitmapImage.SetSource(stream);
- imge1.Source = bitmapImage;
- stream.Close();
- }
- }
- }
- //播放/暂停
- private void Button_Click_1(object sender, RoutedEventArgs e)
- {
- if (PlayState.Playing == BackgroundAudioPlayer.Instance.PlayerState)
- BackgroundAudioPlayer.Instance.Pause();
- else
- BackgroundAudioPlayer.Instance.Play();
- }
- //向前
- private void Button_Click_2(object sender, RoutedEventArgs e)
- {
- BackgroundAudioPlayer.Instance.SkipPrevious();
- button2.IsEnabled = false;
- }
- //向后
- private void Button_Click_3(object sender, RoutedEventArgs e)
- {
- BackgroundAudioPlayer.Instance.SkipNext();
- button3.IsEnabled = false;
- }
- }
四、后台文件传输
后台文件传输允许我们实现下载上传文件的功能,他限制系统中同时运行的传输任务不能超过两个,并且下载的文件只能存放在本地文件夹的/shared/transfers目录下。下面我们实现一个后台传输任务,下载博客相册中的一张照片。
[XAML]
- <Grid x:Name="LayoutRoot" Background="Transparent">
- <Grid.RowDefinitions>
- <RowDefinition Height="Auto"/>
- <RowDefinition Height="*"/>
- </Grid.RowDefinitions>
- <!--TitlePanel 包含应用程序的名称和页标题-->
- <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
- <TextBlock x:Name="ApplicationTitle" Text="后台传输" Style="{StaticResource PhoneTextNormalStyle}"/>
- </StackPanel>
- <!--ContentPanel - 在此处放置其他内容-->
- <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
- <TextBlock x:Name="textblock1" HorizontalAlignment="Left" Margin="10,198,0,0" TextWrapping="Wrap" VerticalAlignment="Top"/>
- <Button Content="清除传输队列中已完成的任务" HorizontalAlignment="Left" Margin="0,85,0,0" VerticalAlignment="Top" Click="Button_Click_2"/>
- </Grid>
- <Button x:Name="button1" Content="添加一个后台传输" HorizontalAlignment="Left" Margin="12,10,0,0" Grid.Row="1" VerticalAlignment="Top" Click="Button_Click_1"/>
- </Grid>
[C#]
- public partial class MainPage : PhoneApplicationPage
- {
- // 构造函数
- public MainPage()
- {
- InitializeComponent();
- }
- protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
- {
- initTransferRequest();
- base.OnNavigatedTo(e);
- }
- private void initTransferRequest()
- {
- //获取第一个后台传输任务
- var transferRequest = BackgroundTransferService.Requests.FirstOrDefault();
- if (transferRequest == null)
- {
- textblock1.Text = "无后台传输任务";
- button1.IsEnabled = true;
- return;
- }
- //当传输状态改变时:
- transferRequest.TransferStatusChanged += new EventHandler<BackgroundTransferEventArgs>(transfer_TransferStatusChanged);
- //当传输进度改变时:
- transferRequest.TransferProgressChanged += new EventHandler<BackgroundTransferEventArgs>(transfer_TransferProgressChanged);
- updatesStatus(transferRequest);
- button1.IsEnabled = false;
- }
- void transfer_TransferStatusChanged(object sender, BackgroundTransferEventArgs e)
- {
- updatesStatus(e.Request);
- }
- void transfer_TransferProgressChanged(object sender, BackgroundTransferEventArgs e)
- {
- updatesStatus(e.Request);
- }
- void updatesStatus(BackgroundTransferRequest transferRequest)
- {
- textblock1.Text = "传输状态:" + transferRequest.TransferStatus.ToString()
- + " 已下载字节:" + transferRequest.BytesReceived
- + "总字节:" + transferRequest.TotalBytesToReceive;
- }
- private void Button_Click_1(object sender, RoutedEventArgs e)
- {
- string fileurlstring = "http://images.cnblogs.com/cnblogs_com/lipan/319399/o_Large.png";
- Uri uri = new Uri(Uri.EscapeUriString(fileurlstring), UriKind.RelativeOrAbsolute);
- BackgroundTransferRequest transferRequest = new BackgroundTransferRequest(uri);
- transferRequest.Method = "GET";
- using (IsolatedStorageFile isoStore = IsolatedStorageFile.GetUserStoreForApplication())
- {
- if (!isoStore.DirectoryExists("/shared/transfers"))
- {
- isoStore.CreateDirectory("/shared/transfers");
- }
- }
- //文件下载后存放位置(为本地文件夹相对位置)
- transferRequest.DownloadLocation = new Uri("shared/transfers/1.png", UriKind.RelativeOrAbsolute);
- //外接电源、WiFi的可用性对传输的影响
- transferRequest.TransferPreferences = TransferPreferences.AllowCellularAndBattery;
- try
- {
- //添加到后台传输队列中
- BackgroundTransferService.Add(transferRequest);
- }
- catch (Exception ex)
- {
- MessageBox.Show("无法添加后台传输请求。" + ex.Message);
- }
- initTransferRequest();
- }
- //清除传输队列已完成的任务
- private void Button_Click_2(object sender, RoutedEventArgs e)
- {
- foreach (var transferRequest in BackgroundTransferService.Requests)
- {
- if (transferRequest.TransferStatus == TransferStatus.Completed)
- {
- try
- {
- BackgroundTransferService.Remove(transferRequest);
- }
- catch
- {
- }
- }
- }
- initTransferRequest();
- }
- }
五、后台辅助线程
后台辅助线程虽然名字这么叫,但是它不能在后台运行,我们可以用它来执行一个任务,并且可以实时获取执行的进度,实现代码如下:
[XAML]
- <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
- <StackPanel>
- <StackPanel Orientation="Horizontal"
- HorizontalAlignment="Left" VerticalAlignment="Top"
- Margin="10" >
- <Button x:Name="buttonStart" Content="开始" Click="buttonStart_Click"
- Width="200" />
- <Button x:Name="buttonCancel" Content="取消" Click="buttonCancel_Click"
- Width="200" />
- </StackPanel>
- <StackPanel Margin="10,50,0,0" Orientation="Horizontal">
- <TextBlock Text="进度: " />
- <TextBlock x:Name="tbProgress" />
- </StackPanel>
- </StackPanel>
- </Grid>
[C#]
- public partial class MainPage : PhoneApplicationPage
- {
- private BackgroundWorker bw = new BackgroundWorker();
- public MainPage()
- {
- InitializeComponent();
- bw.WorkerReportsProgress = true;
- bw.WorkerSupportsCancellation = true;
- bw.DoWork += new DoWorkEventHandler(bw_DoWork);
- bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
- bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
- }
- private void buttonStart_Click(object sender, RoutedEventArgs e)
- {
- if (bw.IsBusy != true)
- {
- bw.RunWorkerAsync();
- }
- }
- private void buttonCancel_Click(object sender, RoutedEventArgs e)
- {
- if (bw.WorkerSupportsCancellation == true)
- {
- bw.CancelAsync();
- }
- }
- private void bw_DoWork(object sender, DoWorkEventArgs e)
- {
- BackgroundWorker worker = sender as BackgroundWorker;
- for (int i = 1; i <= 10; i++)
- {
- if ((worker.CancellationPending == true))
- {
- e.Cancel = true;
- break;
- }
- else
- {
- // Perform a time consuming operation and report progress.
- System.Threading.Thread.Sleep(500);
- worker.ReportProgress(i * 10);
- }
- }
- }
- private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
- {
- if (e.Cancelled == true)
- {
- this.tbProgress.Text = "Canceled!";
- }
- else if (!(e.Error == null))
- {
- this.tbProgress.Text = ("Error: " + e.Error.Message);
- }
- else
- {
- this.tbProgress.Text = "Done!";
- }
- }
- private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
- {
- this.tbProgress.Text = (e.ProgressPercentage.ToString() + "%");
- }
- }
Windows phone 8 学习笔记(6) 多任务(转)的更多相关文章
- Windows phone 8 学习笔记(6) 多任务
原文:Windows phone 8 学习笔记(6) 多任务 Windows phone 8 是一个单任务操作系统,任何时候都只有一个应用处于活跃状态,这里的多任务是指对后台任务的支持.本节我们先讲讲 ...
- Windows phone 8 学习笔记
Windows phone 8 学习笔记(1) 触控输入 http://www.apkbus.com/android-138547-1-1.html Windows phone 8 学习笔记(2) ...
- Windows phone 8 学习笔记(5) 图块与通知(转)
基于metro风格的Windows phone 8 应用提到了图块的概念,它就是指启动菜单中的快速启动图标.一般一个应用必须有一个默认图块,还可以有若干个次要图块.另外,通知与图块的关系比较密切,我们 ...
- Windows phone 8 学习笔记(7) 设备
原文:Windows phone 8 学习笔记(7) 设备 本节主要涉及到 Windows phone 8 手机支持的各类设备,包括相机.设备状态,振动装置等.还有各类感应器,包括磁力计.加速度器和陀 ...
- Windows phone 8 学习笔记(5) 图块与通知
原文:Windows phone 8 学习笔记(5) 图块与通知 基于metro风格的Windows phone 8 应用提到了图块的概念,它就是指启动菜单中的快速启动图标.一般一个应用必须有一个默认 ...
- Windows phone 8 学习笔记(2) 数据文件操作(转)
Windows phone 8 应用用于数据文件存储访问的位置仅仅限于安装文件夹.本地文件夹(独立存储空间).媒体库和SD卡四个地方.本节主要讲解它们的用法以及相关限制性.另外包括本地数据库的使用方式 ...
- Windows phone 8 学习笔记(8) 定位地图导航
原文:Windows phone 8 学习笔记(8) 定位地图导航 Windows phone 8 已经不使用自家的bing地图,新地图控件可以指定制图模式.视图等.bing地图的定位误差比较大,在模 ...
- Windows phone 8 学习笔记(9) 集成
原文:Windows phone 8 学习笔记(9) 集成 本节整理了之前并没有提到的Windows phone 8 系统相关集成支持,包括选择器.锁定屏幕的.联系人的访问等.选择器列举了若干内置应用 ...
- Windows phone 8 学习笔记(2) 数据文件操作
原文:Windows phone 8 学习笔记(2) 数据文件操作 Windows phone 8 应用用于数据文件存储访问的位置仅仅限于安装文件夹.本地文件夹(独立存储空间).媒体库和SD卡四个地方 ...
随机推荐
- CE_现金银行对账单的手工导入和调节(案例)
2014-07-14 Created By BaoXinjian
- T4批量生成多文件
http://www.cnblogs.com/zengxiangzhan/p/3250105.html Manager.ttinclude <#@ assembly name="Sys ...
- 基于jquery的表单校验插件 - formvalidator使用体验
下载地址:http://www.formvalidator.net/ 基本样例 <form action="/registration" method="POST& ...
- 通过Application传递数据代码
使用Application传递数据步骤如下:创建新class,取名MyApp,继承android.app.Application父类,并在MyApp中定义需要保存的属性 在整个Android程 ...
- Maven详解之仓库------本地仓库、远程仓库
在Maven中,任何一个依赖.插件或者项目构建的输出,都可以称之为构件. Maven在某个统一的位置存储所有项目的共享的构件,这个统一的位置,我们就称之为仓库.(仓库就是存放依赖和插件的地方) 任何的 ...
- chrome调试js工具的使用
Audits标签页 这个对于优化前端页面.加速网页加载速度很有用哦(相当与Yslow): 点击run按钮,就可以开始分析页面,分析完了就可以看到分析结果了: 它甚至可以分析出页面上样式表中有哪些CSS ...
- 配置FileZilla Ftp服务器
FileZilla是我比较喜欢用的一款FTP服务端和客户端,主要使用在Windows下,她是一款开源的FTP软件,虽然在某些功能上比不上收费软件Ser-u,但她也是一款非常好用的软件,这里主要说一下这 ...
- C++学习47 文件的概念 文件流类与文件流对象 文件的打开与关闭
迄今为止,我们讨论的输入输出是以系统指定的标准设备(输入设备为键盘,输出设备为显示器)为对象的.在实际应用中,常以磁盘文件作为对象.即从磁盘文件读取数据,将数据输出到磁盘文件.磁盘是计算机的外部存储器 ...
- C++学习41 exception类
C++语言本身或者标准库抛出的异常都是 exception 的子类,称为标准异常(Standard Exception).你可以通过下面的语句来匹配所有标准异常: try{ //可能抛出异常的语句 } ...
- photoshop CS 调整选择区域的大小
网上看到说:矩形选框不能直接调整大小,如果你不想重新画一个可以利用转换路径,然后再调整.这是不对的,矩形选框是可以调整大小的,使用"变换选区"即可. 对应步骤截图如下: 1.画 ...